Python设计医院可视化病历管理系统基于Tkinter+SQLite3(第28节)


随着现代科技的快速发展,电子信息化管理已经渗透到各个行业,包括销售领域、教育领域、医疗领域等。在医疗领域,传统的纸质病历管理方式已无法满足现代医疗机构的需求,存在书写效率低、易丢失、难查询、信息难以共享等问题。本节教程将介绍如何通过Python设计一个简单的医院可视化病历管理系统,通过电子化管理病历和存储信息,提升医疗机构的处理效率并保障数据安全。

1、医院可视化病历管理系统介绍

该系统是一个基于Python的桌面程序,利用Tkinter库创建直观易用的GUI图形界面,实现对病历数据的存储、管理和查询功能。利用Python的第三方库Matplotlib,实现病历数据的可视化图表显示。

可视化病历管理系统的主要功能

病历信息管理功能:支持病历信息的录入、查询、修改和删除功能。病历信息的录入包括患者姓名、年龄、性别、诊断结果、主治医生,信息录入后自动记录就诊时间,并且支持将病历数据列表导出到Excel文件中。

病历信息显示:使用Treeview表格形式展示所有病历记录。在病历记录中支持双击操作,左键双击鼠标可以直接打开修改病历窗口;同样支持右击操作,在病历记录中右击鼠标可以删除单条病历信息。

病历信息查询: 支持高级搜索,输入患者姓名、最小年龄、最大年龄、性别、诊断结果、主治医生等任意条件进行高级搜索,使用Python标准库中的SQLite3数据库存储病历信息,无需额外安装数据库。

数据库存储:使用SQLite3数据库替代JSON文件存储,程序首次启动时,如果在当前脚本工作目录下存在“record_data.db”数据库文件,则程序默认会从该数据库文件中读取已保存的病历数据;如果不存在“record_data.db”数据库文件,则会在当前脚本工作目录下自动创建文件,用于保存病历数据,创建数据库文件过程会自动创建数据库表结构。操作数据库时,系统使用参数化操作,防止SQL注入。

数据可视化:支持病历数据可视化展示,通过Matplotlib库打印全部患者的年龄分布饼图和排名前10种常见疾病柱状图。

2、医院可视化病历管理系统界面演示

Python设计医院可视化病历管理系统基于Tkinter+SQLite3

Python设计医院可视化病历管理系统基于Tkinter+SQLite3

Python设计医院可视化病历管理系统基于Tkinter+SQLite3

3、代码实现

(1)安装pandas库和matplotlib库

首先,确保你已经安装了pandas库和matplotlib库。如果还没有安装,可以通过pip安装:

pip install pandas
pip install matplotlib

(2)导入必要的库

import tkinter as tk
from tkinter import ttk, messagebox, filedialog
import sqlite3, os, webbrowser
import pandas as pd
from datetime import datetime
from matplotlib import rcParams
import matplotlib.pyplot as plt
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg

这里导入了Tkinter库用于创建直观易用的GUI图形界面。pandas库是机器学习四个基础库之一, 它有着强大的数据分析能力和处理工具,这里我们利用pandas库将数据写入Excel文件。Matplotlib库是Python的一个非常流行的绘图库,它提供了2D图表绘制功能,支持饼图、折线图、散点图、直方图等图表,类似MATLAB绘图系统,但属于Python独立开发的第三方库。 ‌

(3)医院可视化病历管理系统,完整代码如下所示:

动手练一练:

import tkinter as tk
from tkinter import ttk, messagebox, filedialog
import sqlite3, os, webbrowser
import pandas as pd
from datetime import datetime
from matplotlib import rcParams
import matplotlib.pyplot as plt
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg

# 获取当前脚本文件所在的目录
record_directory = os.path.dirname(os.path.abspath(__file__))
# 将当前脚本文件所在的目录设置为工作目录
os.chdir(record_directory)

# 定义sqlite3数据库文件为“record_data.db”
data_file = "record_data.db"

# 初始化sqlite3数据库,如果数据库不存在,则会自动创建
try:
    if not os.path.exists(data_file):
        # 创建数据库连接
        def create_data():
            # 使用with语句自动管理数据库连接的生命周期
            with sqlite3.connect(data_file) as conn:
                connection = conn.cursor()

                # 创建病历信息表
                connection.execute('''
                        CREATE TABLE IF NOT EXISTS records (
                        id INTEGER PRIMARY KEY AUTOINCREMENT,
                        name TEXT NOT NULL,
                        age INTEGER,
                        gender TEXT,
                        diagnosis TEXT,
                        doctor TEXT,
                        date TEXT)
                ''')

                conn.commit()  # 提交事务以保存数据库的更改
            conn.close()   # 关闭连接
        # 执行数据库操作
        create_data()
except:
    pass

# 根据ID获取病历信息
def search_records_id(record_id):
    # 使用with语句自动管理数据库连接的生命周期
    with sqlite3.connect(data_file) as conn:
        cursor = conn.cursor()
        # 设置行工厂为Row,允许通过列名访问数据
        cursor.row_factory = sqlite3.Row
        cursor.execute("SELECT * FROM records WHERE id=?", (record_id,))
        row = cursor.fetchone()
        if row:
            result_dict = dict(row)
            return result_dict
        else:
            return None
    conn.close()   # 关闭连接

# 可视化病历管理系统页面
class MedicalManage:
    def __init__(self, root):
        self.root = root
        self.root.title("医院可视化病历管理系统")

        # 设置窗口居中
        window_width = 950
        window_height = 680
        screen_width = self.root.winfo_screenwidth()
        screen_height = self.root.winfo_screenheight()
        x = (screen_width - window_width) / 2
        y = (screen_height - window_height) / 2
        self.root.geometry('%dx%d+%d+%d' % (window_width, window_height, x, y))
        self.root.resizable(width=False, height=False)

        # 创建界面组件
        self.add_widgets()
        # 初始化读取sqlite3数据库表中的所有数据
        self.read_data()

    # 定义可视化病历管理系统组件
    def add_widgets(self):
        # 定义标题标签
        title_label = ttk.Label(self.root, text="医院可视化病历管理系统", font=('宋体', 20, 'bold'))
        title_label.pack(pady=10, padx=10)

        # 病历信息输入区域
        input_frame = ttk.LabelFrame(self.root, text="病历信息输入")
        input_frame.pack(pady=10, padx=10, fill=tk.X)

        # 患者姓名
        tk.Label(input_frame, text="患者姓名:", width = 15).grid(row=0, column=0, padx=5, pady=5)
        self.name_entry = tk.Entry(input_frame, width = 40)
        self.name_entry.grid(row=0, column=1, padx=5, pady=5)

        # 患者年龄
        tk.Label(input_frame, text="年龄:", width = 15).grid(row=1, column=0, padx=5, pady=5)
        self.age_entry = tk.Entry(input_frame, width = 40)
        self.age_entry.grid(row=1, column=1, padx=5, pady=5)

        # 患者性别
        tk.Label(input_frame, text="性别:", width = 15).grid(row=2, column=0, padx=5, pady=5)
        self.gender_entry = tk.Entry(input_frame, width = 40)
        self.gender_entry.grid(row=2, column=1, padx=5, pady=5)

        # 诊断结果
        tk.Label(input_frame, text="诊断结果:", width = 15).grid(row=3, column=0, padx=5, pady=5)
        self.diagnosis_entry = tk.Entry(input_frame, width = 40)
        self.diagnosis_entry.grid(row=3, column=1, padx=5, pady=5)

        # 主治医生
        tk.Label(input_frame, text="主治医生:", width = 15).grid(row=4, column=0, padx=5, pady=5)
        self.doctor_entry = tk.Entry(input_frame, width = 40)
        self.doctor_entry.grid(row=4, column=1, padx=5, pady=5)

        # 创建按钮
        add_button = ttk.Button(input_frame, text="添加病历", command=self.add_record)
        add_button.grid(row=5, column=0, padx=10, pady=10, sticky="e")
        clear_button = ttk.Button(input_frame, text="清空输入", command=self.enter_data_clear)
        clear_button.grid(row=5, column=1, padx=10, pady=10, sticky="w")

        # 病历查询区域
        search_input_frame = ttk.LabelFrame(self.root, text="病历查询")
        search_input_frame.pack(pady=10, padx=10, fill=tk.X)

        # 患者姓名
        tk.Label(search_input_frame, text="患者姓名:").pack(side=tk.LEFT, padx=3)
        search_name_entry = tk.Entry(search_input_frame, width = 8)
        search_name_entry.pack(side=tk.LEFT, padx=3)

        # 最小年龄
        tk.Label(search_input_frame, text="最小年龄:").pack(side=tk.LEFT, padx=3)
        search_min_age_entry = tk.Entry(search_input_frame, width = 4)
        search_min_age_entry.pack(side=tk.LEFT, padx=3)

        # 最大年龄
        tk.Label(search_input_frame, text="最大年龄:").pack(side=tk.LEFT, padx=3)
        search_max_age_entry = tk.Entry(search_input_frame, width = 4)
        search_max_age_entry.pack(side=tk.LEFT, padx=3)

        # 患者性别
        tk.Label(search_input_frame, text="性别:").pack(side=tk.LEFT, padx=3)
        search_gender_entry = tk.Entry(search_input_frame, width = 4)
        search_gender_entry.pack(side=tk.LEFT, padx=3)

        # 诊断结果
        tk.Label(search_input_frame, text="诊断结果:").pack(side=tk.LEFT, padx=3)
        search_diagnosis_entry = tk.Entry(search_input_frame, width = 20)
        search_diagnosis_entry.pack(side=tk.LEFT, padx=3)

        # 主治医生
        tk.Label(search_input_frame, text="主治医生:").pack(side=tk.LEFT, padx=3)
        search_doctor_entry = tk.Entry(search_input_frame, width = 8)
        search_doctor_entry.pack(side=tk.LEFT, padx=3)

        # 开始查询
        def start_search():
            # 定义空列表,用于保存查询条件
            conditions = []
            params = []

            if search_name_entry.get():
                conditions.append("name LIKE ?")
                params.append(f"%{search_name_entry.get()}%")

            if search_min_age_entry.get():
                # 判断是否有效整数
                try:
                    min_results = int(search_min_age_entry.get())
                    conditions.append("age >= ?")
                    params.append(min_results)
                except ValueError:
                    messagebox.showwarning("警告", "年龄请输入有效的整数!")
                    return
            if search_max_age_entry.get():
                # 判断是否有效整数
                try:
                    max_results = int(search_max_age_entry.get())
                    conditions.append("age <= ?")
                    params.append(max_results)
                except ValueError:
                    messagebox.showwarning("警告", "年龄请输入有效的整数!")
                    return

            if search_gender_entry.get():
                conditions.append("gender = ?")
                params.append(search_gender_entry.get())

            if search_diagnosis_entry.get():
                conditions.append("diagnosis LIKE ?")
                params.append(f"%{search_diagnosis_entry.get()}%")

            if search_doctor_entry.get():
                conditions.append("doctor LIKE ?")
                params.append(f"%{search_doctor_entry.get()}%")

            query = "SELECT * FROM records"
            if conditions:
                query += " WHERE " + " AND ".join(conditions)

            # 执行sqlite3数据库查询操作
            cursor = self.command_execute(query, params)
            if cursor:
                self.record_tree.delete(*self.record_tree.get_children())
                for row in cursor.fetchall():
                    self.record_tree.insert("", "end", values=row)
                    # “汇总”统计输出
                    self.record_count.delete(1.0, tk.END)
                    self.record_count.insert(tk.END, len(self.record_tree.get_children()))
                if len(self.record_tree.get_children()) == 0:
                    self.record_count.delete(1.0, tk.END)
                    self.record_count.insert(tk.END, 0)
                    messagebox.showwarning('提示', '没有查询到任何结果!')

        search_button = ttk.Button(search_input_frame, text="查询", command=start_search, width=5)
        search_button.pack(side=tk.LEFT, padx=5, pady=5)

        # 创建“汇总”信息,显示病历的数量
        self.record_count = tk.Text(search_input_frame, relief=tk.FLAT, bg='#f0f0f0', font=10, width=2, height=1)
        self.record_count.pack(side=tk.RIGHT)
        total_label = tk.Label(search_input_frame, text='汇总:', font=10)
        total_label.pack(side=tk.RIGHT)

        # 显示病历列表区域
        show_list = tk.Frame(self.root)
        show_list.pack(fill=tk.X, padx=10, pady=10)

        columns = ("id", "name", "age", "gender", "diagnosis", "doctor", "date")
        # 利用Treeview控件显示病历列表
        self.record_tree = ttk.Treeview(show_list, columns=columns, show='headings')
        self.record_tree.heading('id', text='ID')
        self.record_tree.heading('name', text='患者姓名')
        self.record_tree.heading('age', text='年龄')
        self.record_tree.heading('gender', text='性别')
        self.record_tree.heading('diagnosis', text='诊断结果')
        self.record_tree.heading('doctor', text='主治医生')
        self.record_tree.heading('date', text='就诊日期')

        self.record_tree.column('id', width=30, anchor="center")
        self.record_tree.column('name', width=70, anchor="center")
        self.record_tree.column('age', width=30, anchor="center")
        self.record_tree.column('gender', width=30, anchor="center")
        self.record_tree.column('diagnosis', width=200, anchor="center")
        self.record_tree.column('doctor', width=70, anchor="center")
        self.record_tree.column('date', width=120, anchor="center")
        self.record_tree.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)

        # 创建滚动条控件
        scrollbar = ttk.Scrollbar(show_list, orient=tk.VERTICAL)
        scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
        self.record_tree.configure(yscrollcommand=scrollbar.set)
        scrollbar.config(command=self.record_tree.yview)

        # 鼠标双击病历列表任意一行,可以触发修改病历窗口
        def record_double_click(event):
            # 获取被双击的项的IID
            record_iid = self.record_tree.focus()
            if record_iid == '':
                return  # 如果没有选中任何一行,则直接返回

            # 选中指定的行
            self.record_tree.selection_set((record_iid,))
            self.update_window()

        # 绑定鼠标双击事件
        self.record_tree.bind("<Double-1>", record_double_click)

        # 鼠标右键点击病历列表任意一行,就会显示“删除该病历”按钮
        def record_right_click(event):
            # 获取当前焦点项的record_iid
            record_iid = self.record_tree.focus()
            # 选中指定的行
            self.record_tree.selection_set((record_iid,))

            if record_iid:
                # 弹出菜单选项
                menu = tk.Menu(self.record_tree, tearoff=0)
                menu.add_command(label="删除该病历", command=self.delete_record)
                menu.tk_popup(event.x_root, event.y_root)

        # 绑定鼠标右键点击事件
        self.record_tree.bind("<Button-3>", record_right_click)

        # 病历操作按钮
        button_frame = ttk.Frame(self.root)
        button_frame.pack(fill=tk.X, pady=5)

        refresh_button = ttk.Button(button_frame, text="显示所有病历", command=self.read_data)
        refresh_button.pack(side=tk.LEFT, padx=10)
        update_button = ttk.Button(button_frame, text="修改病历", command=self.update_window)
        update_button.pack(side=tk.LEFT, padx=10)
        delete_button = ttk.Button(button_frame, text="删除病历", command=self.delete_record)
        delete_button.pack(side=tk.LEFT, padx=10)
        statistics_button = ttk.Button(button_frame, text="数据可视化", command=self.statistics_view)
        statistics_button.pack(side=tk.LEFT, padx=10)
        export_button = ttk.Button(button_frame, text="导出以上病历", command=self.export_record)
        export_button.pack(side=tk.LEFT, padx=10)
        quit_button = ttk.Button(button_frame, text="退出", command=self.window_close)
        quit_button.pack(side=tk.LEFT, padx=10)
        help_button = ttk.Button(button_frame, text="程序讲解", command=self.help_window)
        help_button.pack(side=tk.RIGHT, padx=10)
        about_button = ttk.Button(button_frame, text="关于程序", command=self.medical_record_about)
        about_button.pack(side=tk.RIGHT, padx=10)

        # 将窗口关闭事件与window_close函数关联
        self.root.protocol("WM_DELETE_WINDOW", self.window_close)

    # 定义函数,清空Treeview控件中的全部内容
    def clear_record(self):
        for i in self.record_tree.get_children():
            self.record_tree.delete(i)
        # “汇总”统计输出
        self.record_count.delete(1.0, tk.END)
        self.record_count.insert(tk.END, 0)

    # 导出病历到指定Excel表格
    def export_record(self):
        export_result = self.get_all_rows()
        if not any(len(record_list[0]) > 0 for record_list in export_result):
            messagebox.showwarning("警告", "没有数据可导出")
            return
        try:
            # 自定义列名
            columns = ['ID', '患者姓名', '年龄', '性别', '诊断结果', '主治医生', '就诊日期']
            # 将数据转换为DataFrame
            df = pd.DataFrame(export_result, columns=columns)
            save_path = filedialog.asksaveasfilename(
                defaultextension=".xlsx",
                filetypes=[("Excel文件", "*.xlsx")])
            if save_path:
                df.to_excel(save_path, index=False)
                messagebox.showinfo("成功", "导出成功!")
        except PermissionError:
            messagebox.showerror("错误", "文件被占用,请关闭文件后重试")
        except Exception as e:
            messagebox.showerror("错误", f"导出失败:{str(e)}")

    # 获取病历列表中所有列值组成的元组
    def get_all_rows(self):
            rows = []
            for item in self.record_tree.get_children():
                row = self.record_tree.item(item, 'values')
                rows.append(row)
            return rows

    # 定义sqlite3数据库操作方法
    def command_execute(self, query, parameters=()):
        # 使用with语句自动管理数据库连接的生命周期
        with sqlite3.connect(data_file) as conn:
            cursor = conn.cursor()
            try:
                cursor.execute(query, parameters)
                conn.commit()
                return cursor
            except Exception as e:
                conn.rollback()
                messagebox.showerror("数据库错误", str(e))
                return None
        conn.close()   # 关闭连接

    # 添加病历的主方法
    def add_record(self):
        record = (
            self.name_entry.get(),
            self.age_entry.get(),
            self.gender_entry.get(),
            self.diagnosis_entry.get(),
            self.doctor_entry.get(),
            datetime.now().strftime("%Y-%m-%d %H:%M:%S")
        )

        # 从元组中获取所有的值到列表中
        record_list = list(record)
        # 检查每个输入框是否为空
        for result in record_list:
            if not result:
                messagebox.showwarning("输入错误", "输入的内容不能为空!")
                return
            else:
                # 判断是否有效整数
                try:
                    age_results = int(self.age_entry.get())
                except ValueError:
                    messagebox.showwarning("警告", "年龄请输入有效的整数!")
                    return
        try:
            self.command_execute('''INSERT INTO records 
                               (name, age, gender, diagnosis, doctor, date)
                               VALUES (?,?,?,?,?,?)''', record)
            self.read_data()
            self.enter_data_clear()
            messagebox.showinfo("成功", "病历添加成功!")
        except Exception as e:
            messagebox.showerror("错误", f"病历添加失败:{str(e)}")

    # 修改病历窗口
    def update_window(self):
        selected = self.record_tree.selection()
        if not selected:
            messagebox.showwarning("警告", "请先选择要修改的记录")
            return

        record_id = self.record_tree.item(selected[0])['values'][0]
        record = search_records_id(record_id)

        modify_window = tk.Toplevel(self.root)
        modify_window.title("修改病历")

        # 设置窗口居中
        window_width = 540
        window_height = 450
        screen_width = modify_window.winfo_screenwidth()
        screen_height = modify_window.winfo_screenheight()
        x = (screen_width - window_width) / 2
        y = (screen_height - window_height) / 2
        modify_window.geometry('%dx%d+%d+%d' % (window_width, window_height, x, y))
        modify_window.resizable(width=False, height=False)

        # 创建标题标签控件
        record_title = tk.Label(modify_window, text=">>>修改病历<<<", bg = "white", fg = "black", font = ("宋体", 18), bd = '0', anchor = 'center')
        record_title.place(x = 70, y = 30, width = 400, height = 50)

        # 患者姓名
        tk.Label(modify_window, text="患者姓名:", font = ('宋体', 12), width = 15).place(x = 75, y = 120, anchor = 'nw')
        name_entry = tk.Entry(modify_window, font = ('宋体', 15), width = 25)
        name_entry.insert(0, record['name'])
        name_entry.place(x = 210, y = 120, anchor = 'nw')

        # 年龄
        tk.Label(modify_window, text="年龄:", font = ('宋体', 12), width = 15).place(x = 75, y = 170, anchor = 'nw')
        age_entry = tk.Entry(modify_window, font = ('宋体', 15), width = 25)
        age_entry.insert(0, record['age'])
        age_entry.place(x = 210, y = 170, anchor = 'nw')

        # 性别
        tk.Label(modify_window, text="性别:", font = ('宋体', 12), width = 15).place(x = 75, y = 220, anchor = 'nw')
        gender_entry = tk.Entry(modify_window, font = ('宋体', 15), width = 25)
        gender_entry.insert(0, record['gender'])
        gender_entry.place(x = 210, y = 220, anchor = 'nw')

        # 诊断结果
        tk.Label(modify_window, text="诊断结果:", font = ('宋体', 12), width = 15).place(x = 75, y = 270, anchor = 'nw')
        diagnosis_entry = tk.Entry(modify_window, font = ('宋体', 15), width = 25)
        diagnosis_entry.insert(0, record['diagnosis'])
        diagnosis_entry.place(x = 210, y = 270, anchor = 'nw')

        # 主治医生
        tk.Label(modify_window, text="主治医生:", font = ('宋体', 12), width = 15).place(x = 75, y = 320, anchor = 'nw')
        doctor_entry = tk.Entry(modify_window, font = ('宋体', 15), width = 25)
        doctor_entry.insert(0, record['doctor'])
        doctor_entry.place(x = 210, y = 320, anchor = 'nw')

        # 定义错误提示框
        def show_error(window, txt):
            add = tk.Toplevel(window)
            add.title("错误")
            screen_width, screen_height = add.maxsize()
            # 窗口的宽和高
            width = 300
            height = 100
            centre = '%dx%d+%d+%d' % (width, height, (screen_width - width) / 2,
                                    (screen_height - height) / 2)
            add.geometry(centre)
            tk.Label(add, text=f'{txt}', font=('黑体', 14)).pack(pady=10)

            def confirmm():
                add.destroy()

            # 确定按钮
            tk.Button(add, text='确定', font=('黑体', 12), width=10, command=confirmm).pack(pady=10)

        # 更新病历信息
        def record_update():
            try:
                new_data = (
                name_entry.get(),
                age_entry.get(),
                gender_entry.get(),
                diagnosis_entry.get(),
                doctor_entry.get(),
                record_id
                )
                # 从元组中获取所有的值到列表中
                record_list = list(new_data)
                # 检查每个输入框是否为空
                for result in record_list:
                    if not result:
                        show_error(self.root, "输入的内容不能为空!")
                        return
                    else:
                        # 判断是否有效整数
                        try:
                            age_results = int(age_entry.get())
                        except ValueError:
                            show_error(self.root, "年龄请输入有效的整数!")
                            return
                # 更新数据库内容 
                self.command_execute('''UPDATE records SET
                                name=?, age=?, gender=?, diagnosis=?, doctor=?
                                WHERE id=?''', new_data)
                modify_window.destroy()
                self.read_data()
                messagebox.showinfo("成功", "病历修改成功!")
            except Exception as e:
                messagebox.showerror("错误", f"病历修改失败:{str(e)}")

        # 保存按钮
        save_button = ttk.Button(modify_window, text="保存", command=record_update, width=15)
        save_button.place(x = 125, y = 390, anchor = 'nw')

        # 取消按钮
        cancel_button = ttk.Button(modify_window, text="取消", command=modify_window.destroy, width=15)
        cancel_button.place(x = 300, y = 390, anchor = 'nw')

    # 删除病历
    def delete_record(self):
        selected = self.record_tree.selection()
        if not selected:
            messagebox.showwarning("警告", "请先选择要删除的记录")
            return

        if messagebox.askyesno("确认", "确定要删除这条记录吗?"):
            record_id = self.record_tree.item(selected[0], 'values')[0]
            try:
                self.command_execute('DELETE FROM records WHERE id=?', (record_id,))
                self.read_data()
                messagebox.showinfo("成功", "记录已删除")
            except Exception as e:
                messagebox.showerror("错误", f"删除失败:{str(e)}")

    # 添加数据可视化页面
    def statistics_view(self):
        # 设置全局中文字体(解决中文显示问题)
        rcParams['font.sans-serif'] = ['SimHei']
        # 解决负号显示问题
        rcParams['axes.unicode_minus'] = False
        rcParams['font.size'] = 12
        show_window = tk.Toplevel(self.root)
        show_window.title("数据统计")

        # 患者年龄分布统计
        age_list = [
            ("0-18岁", "age BETWEEN 0 AND 18"),
            ("19-30岁", "age BETWEEN 19 AND 30"),
            ("31-45岁", "age BETWEEN 31 AND 45"),
            ("46-60岁", "age BETWEEN 46 AND 60"),
            ("61岁及以上", "age >= 61")
        ]

        age_data = {}
        for label, condition in age_list:
            cursor = self.command_execute(f"SELECT COUNT(*) FROM records WHERE {condition}")
            age_data[label] = cursor.fetchone()[0]

        # 常见疾病统计
        cursor = self.command_execute(
            "SELECT diagnosis, COUNT(*) FROM records GROUP BY diagnosis ORDER BY COUNT(*) DESC LIMIT 10")
        disease_data = cursor.fetchall()

        # 创建统计图表
        fig = plt.Figure(figsize=(10, 6))

        # 年龄分布饼图
        subplot1 = fig.add_subplot(121)
        labels = list(age_data.keys())
        sizes = list(age_data.values())

        # 过滤掉人数为0的等级
        filtered_labels = []
        filtered_sizes = []
        for label, size in zip(labels, sizes):
            if size > 0:
                filtered_labels.append(label)
                filtered_sizes.append(size)

        if not filtered_sizes:
            subplot1.text(0.5, 0.5, "无数据", ha='center', va='center')
            subplot1.set_title('年龄分布')

        subplot1.pie(filtered_sizes,
                labels=filtered_labels,
                autopct='%1.1f%%',
                startangle=90,
                wedgeprops={'linewidth': 1, 'edgecolor': 'white'},
                textprops={'fontsize': 12})
        subplot1.set_title('年龄分布')
        # 在Figure上调用tight_layout(),自动调整布局
        fig.tight_layout()

        # 常见疾病柱状图
        subplot2 = fig.add_subplot(122)
        diagnoses = [item[0] for item in disease_data]
        counts = [item[1] for item in disease_data]
        subplot2.barh(diagnoses, counts)
        subplot2.set_title('常见疾病TOP10')
        subplot2.invert_yaxis()
        # 在Figure上调用tight_layout(),自动调整布局
        fig.tight_layout()

        # 创建画布,显示可视化图表
        canvas = FigureCanvasTkAgg(fig, master=show_window)
        canvas.draw()
        canvas.get_tk_widget().pack(fill=tk.BOTH, expand=True)

    # 读取数据库表中的所有数据
    def read_data(self):
        self.clear_record()
        cursor = self.command_execute("SELECT * FROM records")
        if cursor:
            self.record_tree.delete(*self.record_tree.get_children())
            for row in cursor.fetchall():
                self.record_tree.insert("", "end", values=row)
                # “汇总”统计输出
                self.record_count.delete(1.0, tk.END)
                self.record_count.insert(tk.END, len(self.record_tree.get_children()))

    # 清空输入的数据
    def enter_data_clear(self):
        self.name_entry.delete(0, "end"),
        self.age_entry.delete(0, "end"),
        self.gender_entry.delete(0, "end"),
        self.diagnosis_entry.delete(0, "end"),
        self.doctor_entry.delete(0, "end")

    # 关于病历管理系统
    def medical_record_about(self):
        about_window = tk.Tk()
        about_window.title('关于本系统')

        # 设置窗口居中
        window_width = 420
        window_height = 420
        screen_width = about_window.winfo_screenwidth()
        screen_height = about_window.winfo_screenheight()
        x = (screen_width - window_width) / 2
        y = (screen_height - window_height) / 2
        about_window.geometry('%dx%d+%d+%d' % (window_width, window_height, x, y))
        about_window.resizable(width=False, height=False)

        about_frame = tk.Frame(about_window, width=420, height=420)
        about_frame.pack()
        tk.Label(about_frame, text='医院可视化病历管理系统', font=("宋体", 16)).place(x=90, y=20)
        tk.Label(about_frame, text='使用编程语言:Python', font=("宋体", 14)).place(x=50, y=70)
        tk.Label(about_frame, text='使用数据库:SQLite3数据库', font=("宋体", 14)).place(x=50, y=120)
        tk.Label(about_frame, text='数据库文件:record_data.db', font=("宋体", 14)).place(x=50, y=170)
        tk.Label(about_frame, text='修改病历:在病历列表中直接双击鼠标', font=("宋体", 14)).place(x=50, y=220)
        tk.Label(about_frame, text='删除病历:在病历列表中右击鼠标', font=("宋体", 14)).place(x=50, y=270)
        tk.Label(about_frame, text='导出病历方式:导出到Excel文件内', font=("宋体", 14)).place(x=50, y=320)
        tk.Label(about_frame, text='创作者:www.pyhint.com', font=("宋体", 14)).place(x=50, y=370)
        about_window.mainloop()

    # 病历管理系统讲解页面
    def help_window(self):
        webbrowser.open("https://www.pyhint.com/article/161.html")

    # 点击关闭窗口触发函数
    def window_close(self):
        # 弹出确认对话框
        if messagebox.askokcancel("退出", "你确定要退出吗?"):
            # 点击确定后关闭窗口
            self.root.destroy()

# 当前模块直接被执行
if __name__ == "__main__":
    # 创建主窗口
    root = tk.Tk()
    app = MedicalManage(root)
    # 开启主循环,让窗口处于显示状态
    root.mainloop()