吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 3068|回复: 72
收起左侧

[Python 转载] 借助AI搞了个电脑定时关机软件,用于寒假控制小孩玩电脑

[复制链接]
fkiss 发表于 2026-2-3 23:07
本帖最后由 fkiss 于 2026-2-4 18:20 编辑

主要功能:

- 时间管理 :每日限额(默认90分钟)、允许时段控制、实时进度显示。
- 行为管控 :应用黑名单拦截(如自动关闭抖音、游戏等)。
- 系统控制 :超时或非允许时段自动关机、手动定时关机。
- 安全保护 :密码验证(默认密码 123456 )、防退出机制。

通过网盘分享的文件:
链接: https://pan.baidu.com/s/1dI6vFHO1_avpOVKOl4G6iQ?pwd=6666 提取码: 6666


1.jpg 2.jpg 3.png

[Asm] 纯文本查看 复制代码
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
儿童电脑管控程序 V1.0
基于Tkinter的桌面应用程序,用于家长管理儿童电脑使用时间
"""

import tkinter as tk
from tkinter import ttk, messagebox, simpledialog
import configparser
import json
import datetime
import time
import threading
import subprocess
import sys
import os
import platform
import psutil
from typing import List, Dict, Tuple, Optional, Any

# 版本信息
VERSION = "1.0.0"
AUTHOR = "J"

# ==========================================
# 核心逻辑层 (Model & Controller)
# ==========================================

class ConfigManager:
    """配置与数据管理器"""
    
    def __init__(self):
        # 路径设置
        if getattr(sys, 'frozen', False):
            self.base_dir = os.path.dirname(sys.executable)
        else:
            self.base_dir = os.path.dirname(os.path.abspath(__file__))
            
        self.config_file = os.path.join(self.base_dir, "config.ini")
        self.usage_file = os.path.join(self.base_dir, "usage_data.json")
        self.blacklist_file = os.path.join(self.base_dir, "app_blacklist.json")
        
        # 运行时状态
        self.today = datetime.date.today().isoformat()
        self.used_seconds = 0
        self.app_blacklist = {}
        
        # 配置项缓存
        self.daily_limit = 90
        self.password = "123456"
        self.password_enabled = True
        self.shutdown_delay = 60
        self.warning_time = 120
        self.time_slots = []
        
        # 加载所有数据
        self.load_all()

    def load_all(self):
        """加载所有配置和数据"""
        self.load_config()
        self.load_usage_data()
        self.load_blacklist()

    def load_config(self):
        """加载 config.ini"""
        config = configparser.ConfigParser()
        
        # 默认配置
        default_config = {
            'TimeLimits': {
                'DailyLimit': '90',
                'Password': '123456',
                'PasswordEnabled': '0', # 1=Enabled, 0=Disabled
                '1': '09:00-23:00',
                '2': '09:00-23:00',
                '3': '09:30-12:00,12:50-23:00',
                '4': '09:30-12:00,12:50-23:00',
                '5': '09:30-12:00,12:50-18:00,19:00-23:30',
                '6': '09:30-12:00,12:50-18:00,19:00-23:30',
                '7': '09:30-12:00,12:50-18:00,19:00-23:30',
            },
            'Settings': {
                'ShutdownDelay': '60',
                'WarningTime': '120',
            }
        }
        
        if not os.path.exists(self.config_file):
            self._create_default_config(config, default_config)
        else:
            try:
                config.read(self.config_file, encoding='utf-8')
                if not config.has_section('TimeLimits'):
                    self._create_default_config(config, default_config)
            except:
                self._create_default_config(config, default_config)
        
        # 解析配置到内存
        try:
            self.daily_limit = config.getint('TimeLimits', 'DailyLimit', fallback=90)
            self.password = config.get('TimeLimits', 'Password', fallback='123456')
            self.password_enabled = config.getboolean('TimeLimits', 'PasswordEnabled', fallback=True)
            
            self.shutdown_delay = config.getint('Settings', 'ShutdownDelay', fallback=60)
            self.warning_time = config.getint('Settings', 'WarningTime', fallback=120)
            
            # 解析今日时间段
            weekday = datetime.datetime.today().weekday() + 1 # 1-7
            time_slot_str = config.get('TimeLimits', str(weekday), fallback="")
            self.time_slots = self._parse_time_slots(time_slot_str)
            
        except Exception as e:
            print(f"配置解析错误: {e}")

    def _create_default_config(self, config, default_data):
        """创建默认配置文件"""
        for section, values in default_data.items():
            if not config.has_section(section):
                config.add_section(section)
            for k, v in values.items():
                config.set(section, k, v)
        try:
            with open(self.config_file, 'w', encoding='utf-8') as f:
                config.write(f)
        except Exception as e:
            print(f"写入配置文件失败: {e}")

    def save_config(self, new_config: Dict):
        """保存配置"""
        config = configparser.ConfigParser()
        config.read(self.config_file, encoding='utf-8')
        
        for section, items in new_config.items():
            if not config.has_section(section):
                config.add_section(section)
            for k, v in items.items():
                config.set(section, str(k), str(v))
        
        try:
            with open(self.config_file, 'w', encoding='utf-8') as f:
                config.write(f)
            self.load_config()
            return True
        except Exception as e:
            print(f"保存配置失败: {e}")
            return False

    def load_usage_data(self):
        """加载使用数据"""
        if os.path.exists(self.usage_file):
            try:
                with open(self.usage_file, 'r', encoding='utf-8') as f:
                    data = json.load(f)
                if data.get('date') == self.today:
                    self.used_seconds = data.get('used_seconds', 0)
                else:
                    self.used_seconds = 0
                    self.save_usage_data()
            except:
                self.used_seconds = 0
        else:
            self.used_seconds = 0
            self.save_usage_data()

    def save_usage_data(self):
        """保存使用数据"""
        data = {
            'date': self.today,
            'used_seconds': self.used_seconds,
            'last_updated': datetime.datetime.now().isoformat()
        }
        try:
            with open(self.usage_file, 'w', encoding='utf-8') as f:
                json.dump(data, f, ensure_ascii=False, indent=2)
        except Exception as e:
            print(f"保存使用数据失败: {e}")

    def load_blacklist(self):
        """加载黑名单"""
        if os.path.exists(self.blacklist_file):
            try:
                with open(self.blacklist_file, 'r', encoding='utf-8') as f:
                    self.app_blacklist = json.load(f)
            except:
                self.app_blacklist = {}
        else:
            # 默认黑名单
            self.app_blacklist = {
                "douyin.exe": {"enabled": True, "process_name": "douyin.exe", "display_name": "抖音"},
                "msedge.exe": {"enabled": False, "process_name": "msedge.exe", "display_name": "EDGE浏览器"},
                "wechat.exe": {"enabled": False, "process_name": "wechat.exe", "display_name": "微信"},
            }
            self.save_blacklist()

    def save_blacklist(self):
        """保存黑名单"""
        try:
            with open(self.blacklist_file, 'w', encoding='utf-8') as f:
                json.dump(self.app_blacklist, f, ensure_ascii=False, indent=2)
        except Exception as e:
            print(f"保存黑名单失败: {e}")

    def check_date_change(self):
        """检查日期变更"""
        current_date = datetime.date.today().isoformat()
        if current_date != self.today:
            self.today = current_date
            self.used_seconds = 0
            self.save_usage_data()
            self.load_config()
            return True
        return False

    def _parse_time_slots(self, time_slot_str: str) -> List[Tuple[int, int]]:
        """解析时间段字符串"""
        slots = []
        if not time_slot_str:
            return slots
        try:
            for slot in time_slot_str.split(','):
                slot = slot.strip()
                if not slot: continue
                start_str, end_str = slot.split('-')
                sh, sm = map(int, start_str.split(':'))
                eh, em = map(int, end_str.split(':'))
                slots.append((sh * 60 + sm, eh * 60 + em))
        except:
            pass
        return slots


class SystemController:
    """系统控制与监控"""
    
    def __init__(self, config_manager: ConfigManager):
        self.cfg = config_manager
        self.is_windows = platform.system() == "Windows"
        self._shutdown_scheduled = False
        self._shutdown_deadline = 0 
    
    def shutdown(self, delay: int = None, reason: str = "使用时间结束"):
        """执行系统关机"""
        if delay is None:
            delay = self.cfg.shutdown_delay
            
        print(f"执行关机: {delay}秒后, 原因: {reason}")
        self._shutdown_scheduled = True
        self._shutdown_deadline = int(time.time() + delay)
        
        if self.is_windows:
            subprocess.run(f'shutdown /s /t {delay} /c "{reason}"', shell=True)
        else:
            minutes = max(1, delay // 60)
            subprocess.run(['shutdown', '-h', f'+{minutes}'], check=False)

    def cancel_shutdown(self):
        """取消系统关机"""
        if self._shutdown_scheduled:
            print("取消关机")
            if self.is_windows:
                subprocess.run('shutdown /a', shell=True)
            else:
                subprocess.run(['shutdown', '-c'], check=False)
            
            self._shutdown_scheduled = False
            self._shutdown_deadline = 0

    def is_shutdown_scheduled(self):
        return self._shutdown_scheduled

    def get_manual_shutdown_time_str(self) -> str:
        """获取手动/已计划关机时间字符串 HH:MM"""
        if not self._shutdown_scheduled:
            return ""
        dt = datetime.datetime.fromtimestamp(self._shutdown_deadline)
        return dt.strftime("%H:%M")

    def kill_blacklisted_apps(self):
        """检查并关闭黑名单应用"""
        if not self.cfg.app_blacklist:
            return []
            
        killed_apps = []
        target_names = {
            info['process_name'].lower() 
            for info in self.cfg.app_blacklist.values() 
            if info.get('enabled', False)
        }
        
        if not target_names:
            return []

        try:
            for proc in psutil.process_iter(['pid', 'name']):
                try:
                    p_name = proc.info['name'].lower()
                    for target in target_names:
                        if target in p_name:
                            proc.kill()
                            killed_apps.append(p_name)
                            break
                except (psutil.NoSuchProcess, psutil.AccessDenied):
                    continue
        except Exception as e:
            pass
            
        return killed_apps

# ==========================================
# 用户界面层 (View)
# ==========================================

class ChildControlApp:
    """主应用程序"""
    
    def __init__(self, root: tk.Tk):
        self.root = root
        self.cfg = ConfigManager()
        self.sys_ctrl = SystemController(self.cfg)
        
        # 状态标志
        self.manual_shutdown_mode = False 
        self.is_allowed_now = False
        
        self.setup_ui()
        self.start_timers()
        
    def setup_ui(self):
        """初始化界面"""
        self.root.title(f"儿童电脑使用时间管控 v{VERSION}")
        self.root.geometry("375x400")
        self.root.resizable(False, False)
        
        style = ttk.Style()
        if platform.system() == "Windows":
            style.theme_use('vista')
        
        # 主容器
        main_frame = tk.Frame(self.root, padx=15, pady=15, bg="#f0f0f0")
        main_frame.pack(fill=tk.BOTH, expand=True)
        self.root.configure(bg="#f0f0f0")
        
        # 1. 标题区
        header_frame = tk.Frame(main_frame, bg="#f0f0f0")
        header_frame.pack(fill=tk.X, pady=(0, 10))
        
        tk.Label(header_frame, text="儿童电脑使用时间管控", font=("微软雅黑", 14, "bold"), 
                 bg="#f0f0f0", fg="#0d6efd").pack()
        
        self.time_display = tk.Label(header_frame, text="Loading...", font=("Consolas", 10, "bold"),
                                     bg="#0d6efd", fg="white", padx=10, pady=4, relief=tk.RAISED)
        self.time_display.pack(pady=(8, 0))
        
        # 2. 状态信息面板
        self.status_frame = tk.LabelFrame(main_frame, text="状态: 初始化中...", font=("微软雅黑", 10, "bold"),
                                         bg="#f0f0f0", fg="#0d6efd", padx=10, pady=5)
        self.status_frame.pack(fill=tk.X, pady=(0, 10))
        
        self.lbl_blacklist = self._create_status_row(self.status_frame, "禁用应用:", "无")
        self.lbl_slots = self._create_status_row(self.status_frame, "允许时段:", "--:--")
        self.lbl_shutdown_time = self._create_status_row(self.status_frame, "关机时间:", "--:--")
        self.lbl_progress_text = self._create_status_row(self.status_frame, "今日进度:", "0 / 90 分钟")
        
        self.progress_canvas = tk.Canvas(self.status_frame, height=12, bg="#e9ecef", relief=tk.SUNKEN, bd=1)
        self.progress_canvas.pack(fill=tk.X, pady=(5, 5))
        
        # 3. 提示信息区
        self.lbl_info = tk.Label(main_frame, text="系统正常运行中", font=("微软雅黑", 9),
                                bg="#f0f0f0", fg="green", wraplength=340)
        self.lbl_info.pack(pady=(0, 10))
        
        # 4. 控制按钮区 (合并为一行)
        btn_frame = tk.Frame(main_frame, bg="#f0f0f0")
        btn_frame.pack(fill=tk.X, side=tk.BOTTOM, pady=10)
        
        # 顺序:手动关机 | 禁用应用 | 修改设置
        self._create_btn(btn_frame, "🛑 手动关机", self.show_manual_shutdown, "#dc3545").pack(side=tk.LEFT, fill=tk.X, expand=True, padx=(0, 5))
        self._create_btn(btn_frame, "🚫 禁用应用", self.show_blacklist, "#17a2b8").pack(side=tk.LEFT, fill=tk.X, expand=True, padx=(0, 5))
        self._create_btn(btn_frame, "⚙️ 修改设置", self.show_settings, "#6c757d").pack(side=tk.LEFT, fill=tk.X, expand=True)

        # 拦截关闭事件,执行退出逻辑
        self.root.protocol("WM_DELETE_WINDOW", self.exit_app)

    def _create_status_row(self, parent, label, value):
        frame = tk.Frame(parent, bg="#f0f0f0")
        frame.pack(fill=tk.X, pady=1)
        tk.Label(frame, text=label, width=9, anchor="w", bg="#f0f0f0", font=("微软雅黑", 9)).pack(side=tk.LEFT)
        val_lbl = tk.Label(frame, text=value, anchor="w", bg="#f0f0f0", font=("微软雅黑", 9))
        val_lbl.pack(side=tk.LEFT, fill=tk.X, expand=True)
        return val_lbl

    def _create_btn(self, parent, text, command, color):
        return tk.Button(parent, text=text, command=command, bg=color, fg="white", 
                        font=("微软雅黑", 9), relief=tk.RAISED, bd=0, padx=2, pady=8, cursor="hand2",
                        activebackground=color, activeforeground="white")

    # ================= 业务逻辑 =================

    def start_timers(self):
        self.timer_loop()
        threading.Thread(target=self.app_monitor_loop, daemon=True).start()

    def timer_loop(self):
        now = datetime.datetime.now()
        
        # 1. 更新顶部时间
        date_str = now.strftime("%Y-%m-%d")
        week_str = ["周一", "周二", "周三", "周四", "周五", "周六", "周日"][now.weekday()]
        time_str = now.strftime("%H:%M:%S")
        self.time_display.config(text=f"{date_str} {week_str} {time_str}")
        
        # 2. 跨天检查
        if self.cfg.check_date_change():
            self.manual_shutdown_mode = False 
            self.sys_ctrl.cancel_shutdown()
        
        # 3. 权限检查
        current_minutes = now.hour * 60 + now.minute
        
        in_time_slot = False
        current_slot_end = -1
        
        for start, end in self.cfg.time_slots:
            if start <= current_minutes < end:
                in_time_slot = True
                current_slot_end = end
                break
        
        limit_reached = self.cfg.used_seconds >= (self.cfg.daily_limit * 60)
        
        # --- 修改:计时逻辑只要运行就累加 ---
        self.cfg.used_seconds += 1
        if self.cfg.used_seconds % 60 == 0:
            self.cfg.save_usage_data()
        
        # 状态判断
        if self.manual_shutdown_mode:
            self.status_frame.config(text="状态: 手动计划执行中")
            self.is_allowed_now = True 
        else:
            if not in_time_slot:
                self.is_allowed_now = False
                self.status_frame.config(text="状态: 禁止使用 (非时段)")
            elif limit_reached:
                self.is_allowed_now = False
                self.status_frame.config(text="状态: 禁止使用 (超限额)")
            else:
                self.is_allowed_now = True
                self.status_frame.config(text="状态: 允许使用")

        # 4. 关机逻辑控制
        self._handle_shutdown_logic(limit_reached, in_time_slot)
        
        # 5. 更新UI信息
        self._update_status_ui(now, current_minutes, current_slot_end)
        
        self.root.after(1000, self.timer_loop)

    def _handle_shutdown_logic(self, limit_reached, in_time_slot):
        if self.manual_shutdown_mode:
            return

        if not self.is_allowed_now:
            if not self.sys_ctrl.is_shutdown_scheduled():
                reason = "使用时间已耗尽" if limit_reached else "非允许使用时段"
                self.sys_ctrl.shutdown(self.cfg.shutdown_delay, reason)
        else:
            if self.sys_ctrl.is_shutdown_scheduled():
                self.sys_ctrl.cancel_shutdown()

    def _update_status_ui(self, now_dt, current_minutes, current_slot_end):
        # 更新允许时段
        slots_str = ",".join([f"{s//60:02d}:{s%60:02d}-{e//60:02d}:{e%60:02d}" for s, e in self.cfg.time_slots])
        self.lbl_slots.config(text=slots_str if slots_str else "无")
        
        # 更新黑名单
        enabled_apps = [v['display_name'] for v in self.cfg.app_blacklist.values() if v.get('enabled')]
        self.lbl_blacklist.config(text=",".join(enabled_apps[:2]) + ("..." if len(enabled_apps)>2 else "") or "无")
        
        # 更新进度
        limit_mins = self.cfg.daily_limit
        used_mins = self.cfg.used_seconds // 60
        
        if used_mins > limit_mins:
            over_mins = used_mins - limit_mins
            self.lbl_progress_text.config(text=f"{used_mins}分 / {limit_mins}分 (超时 {over_mins}分)")
        else:
            remain_mins = max(0, limit_mins - used_mins)
            self.lbl_progress_text.config(text=f"{used_mins}分 / {limit_mins}分 (剩余 {remain_mins}分)")
        
        # 绘制进度条
        pct = min(1.0, self.cfg.used_seconds / (limit_mins * 60)) if limit_mins > 0 else 1.0
        if self.cfg.used_seconds > (limit_mins * 60):
            pct = 1.0
            
        w = self.progress_canvas.winfo_width()
        self.progress_canvas.delete("all")
        color = "#28a745" # Green
        if pct > 0.8: color = "#ffc107" # Yellow
        if pct > 0.95: color = "#dc3545" # Red
        if used_mins > limit_mins: color = "#dc3545"
            
        self.progress_canvas.create_rectangle(0, 0, w * pct, 20, fill=color, outline="")
        
        # --- 计算并显示关机时间 (优化跳动问题) ---
        shutdown_time_str = "--:--"
        
        if self.sys_ctrl.is_shutdown_scheduled():
            shutdown_time_str = self.sys_ctrl.get_manual_shutdown_time_str()
        elif self.is_allowed_now:
            minutes_until_slot_end = 9999
            if current_slot_end != -1:
                minutes_until_slot_end = current_slot_end - current_minutes
            
            remain_seconds = max(0, (limit_mins * 60) - self.cfg.used_seconds)
            # 使用 ceil 向上取整分钟,或者加 59秒 再整除,避免 59s 显示少1分钟
            minutes_until_limit = (remain_seconds + 59) // 60
            
            actual_minutes_left = min(minutes_until_slot_end, minutes_until_limit)
            
            if actual_minutes_left < 9999:
                # 统一去掉秒数的影响,只按当前分钟整点计算
                base_time = now_dt.replace(second=0, microsecond=0)
                shutdown_dt = base_time + datetime.timedelta(minutes=actual_minutes_left)
                shutdown_time_str = shutdown_dt.strftime("%H:%M")
        
        self.lbl_shutdown_time.config(text=shutdown_time_str)
        
        # 更新警告信息
        if self.sys_ctrl.is_shutdown_scheduled():
             # 修改警告样式
             self.lbl_info.config(text=f"&#9888;&#65039; 系统将在 {shutdown_time_str} 关机", fg="red")
        elif (limit_mins - used_mins) < (self.cfg.warning_time // 60):
             remain = max(0, limit_mins - used_mins)
             self.lbl_info.config(text=f"&#9888;&#65039; 剩余时间不足 {remain} 分钟", fg="#ff9900")
        else:
             self.lbl_info.config(text="&#9989; 正常使用中", fg="green")

    def app_monitor_loop(self):
        while True:
            self.sys_ctrl.kill_blacklisted_apps()
            time.sleep(10)

    # ================= 对话框与交互 =================

    def verify_password(self):
        """密码验证"""
        if not self.cfg.password_enabled:
            return True
        pwd = simpledialog.askstring("密码验证", "请输入管理员密码:", show="*", parent=self.root)
        if pwd == self.cfg.password:
            return True
        if pwd is not None:
            messagebox.showerror("错误", "密码错误")
        return False

    def show_manual_shutdown(self):
        """手动关机对话框"""
        if not self.verify_password(): return
        
        win = tk.Toplevel(self.root)
        win.title("手动关机设置")
        win.geometry("300x250")
        win.resizable(False, False)
        win.grab_set()
        win.geometry(f"+{self.root.winfo_x()+30}+{self.root.winfo_y()+70}")
        
        v_mode = tk.IntVar(value=1) # 1=倒计时, 2=定时
        
        tk.Label(win, text="请选择关机方式:", font=("微软雅黑", 10, "bold")).pack(pady=10)
        
        f1 = tk.Frame(win)
        f1.pack(fill=tk.X, padx=20, pady=5)
        tk.Radiobutton(f1, text="倒计时关机", variable=v_mode, value=1).pack(side=tk.LEFT)
        v_minutes = tk.StringVar(value="30")
        tk.Entry(f1, textvariable=v_minutes, width=5).pack(side=tk.LEFT, padx=5)
        tk.Label(f1, text="分钟后").pack(side=tk.LEFT)
        
        f2 = tk.Frame(win)
        f2.pack(fill=tk.X, padx=20, pady=5)
        tk.Radiobutton(f2, text="指定时间关机", variable=v_mode, value=2).pack(side=tk.LEFT)
        now_next = datetime.datetime.now() + datetime.timedelta(hours=1)
        v_time = tk.StringVar(value=now_next.strftime("%H:%M"))
        tk.Entry(f2, textvariable=v_time, width=8).pack(side=tk.LEFT, padx=5)
        
        def apply():
            try:
                delay = 0
                target_dt = None
                
                if v_mode.get() == 1:
                    mins = int(v_minutes.get())
                    if mins <= 0: raise ValueError
                    delay = mins * 60
                    target_dt = datetime.datetime.now() + datetime.timedelta(seconds=delay)
                else:
                    t_str = v_time.get()
                    th, tm = map(int, t_str.split(":"))
                    now = datetime.datetime.now()
                    target_dt = now.replace(hour=th, minute=tm, second=0, microsecond=0)
                    if target_dt <= now:
                        target_dt += datetime.timedelta(days=1)
                    delay = int((target_dt - now).total_seconds())
                
                # 构造包含时间的提示信息
                time_str = target_dt.strftime("%H:%M")
                reason = f"家长手动设置{time_str}关机"
                
                self.sys_ctrl.cancel_shutdown()
                self.sys_ctrl.shutdown(delay, reason)
                self.manual_shutdown_mode = True
                messagebox.showinfo("成功", f"已设置 {time_str} 关机")
                win.destroy()
            except:
                messagebox.showerror("错误", "时间格式无效")

        def cancel_manual():
            self.sys_ctrl.cancel_shutdown()
            self.manual_shutdown_mode = False
            messagebox.showinfo("提示", "已取消手动关机计划,恢复自动管控")
            win.destroy()

        btn_box = tk.Frame(win, pady=20)
        btn_box.pack()
        tk.Button(btn_box, text="执行关机", command=apply, bg="#dc3545", fg="white").pack(side=tk.LEFT, padx=5)
        tk.Button(btn_box, text="恢复自动", command=cancel_manual).pack(side=tk.LEFT, padx=5)

    def show_settings(self):
        """设置对话框 (单页美化版)"""
        if not self.verify_password(): return
        
        win = tk.Toplevel(self.root)
        win.title("系统设置")
        win.geometry("380x450")
        win.resizable(False, False)
        win.grab_set()
        win.geometry(f"+{self.root.winfo_x()+10}+{self.root.winfo_y()+10}")
        
        # 容器
        container = tk.Frame(win, padx=15, pady=15)
        container.pack(fill=tk.BOTH, expand=True)
        
        # 1. 常规设置
        lf1 = tk.LabelFrame(container, text="基本设置", font=("微软雅黑", 9, "bold"), fg="#0d6efd", padx=10, pady=10)
        lf1.pack(fill=tk.X, pady=(0, 10))
        
        tk.Label(lf1, text="每日限额 (分钟):").grid(row=0, column=0, sticky=tk.W, pady=5)
        v_limit = tk.StringVar(value=str(self.cfg.daily_limit))
        tk.Entry(lf1, textvariable=v_limit, width=10).grid(row=0, column=1, sticky=tk.W, padx=10)
        
        # 2. 时间段
        lf2 = tk.LabelFrame(container, text="今日时段配置", font=("微软雅黑", 9, "bold"), fg="#0d6efd", padx=10, pady=10)
        lf2.pack(fill=tk.X, pady=(0, 10))
        
        weekday = datetime.datetime.now().weekday() + 1
        tk.Label(lf2, text=f"今天是周{weekday},允许时间段:", anchor=tk.W).pack(fill=tk.X)
        
        config = configparser.ConfigParser()
        config.read(self.cfg.config_file)
        current_slots_str = config.get('TimeLimits', str(weekday), fallback="")
        
        v_slots = tk.StringVar(value=current_slots_str)
        tk.Entry(lf2, textvariable=v_slots).pack(fill=tk.X, pady=5)
        tk.Label(lf2, text="示例: 09:00-12:00,14:00-18:00\n(修改仅今日生效,永久修改请编辑config.ini)", 
                 fg="gray", font=("微软雅黑", 8), justify=tk.LEFT).pack(anchor=tk.W)
        
        # 3. 安全设置
        lf3 = tk.LabelFrame(container, text="安全验证", font=("微软雅黑", 9, "bold"), fg="#0d6efd", padx=10, pady=10)
        lf3.pack(fill=tk.X, pady=(0, 10))
        
        v_pwd_enabled = tk.BooleanVar(value=self.cfg.password_enabled)
        tk.Checkbutton(lf3, text="启用密码保护", variable=v_pwd_enabled).pack(anchor=tk.W)
        
        f_pwd = tk.Frame(lf3)
        f_pwd.pack(fill=tk.X, pady=(5, 0))
        tk.Label(f_pwd, text="管理密码:").pack(side=tk.LEFT)
        v_pwd = tk.StringVar(value=self.cfg.password)
        tk.Entry(f_pwd, textvariable=v_pwd, show="*", width=15).pack(side=tk.LEFT, padx=10)
        
        # 底部按钮
        def save():
            try:
                limit = int(v_limit.get())
                slots = v_slots.get().strip()
                pwd = v_pwd.get().strip()
                
                # 简单验证
                self.cfg._parse_time_slots(slots)
                
                updates = {
                    'TimeLimits': {
                        'DailyLimit': limit,
                        'Password': pwd,
                        'PasswordEnabled': '1' if v_pwd_enabled.get() else '0',
                        str(weekday): slots
                    }
                }
                
                if self.cfg.save_config(updates):
                    messagebox.showinfo("成功", "设置已保存")
                    win.destroy()
                else:
                    messagebox.showerror("错误", "保存失败")
            except Exception as e:
                messagebox.showerror("错误", f"输入无效: {e}")
        
        tk.Button(container, text="保存设置", command=save, bg="#0d6efd", fg="white", 
                 padx=20, pady=5, relief=tk.FLAT).pack(side=tk.BOTTOM)

    def show_blacklist(self):
        """黑名单管理 (内嵌添加功能)"""
        if not self.verify_password(): return
        
        win = tk.Toplevel(self.root)
        win.title("应用黑名单")
        win.geometry("500x500") # 增加高度以容纳提示
        win.grab_set()
        
        # 顶部添加区域
        add_frame = tk.LabelFrame(win, text="添加新规则", padx=10, pady=10)
        add_frame.pack(fill=tk.X, padx=10, pady=10)
        
        f_row = tk.Frame(add_frame)
        f_row.pack(fill=tk.X)
        
        tk.Label(f_row, text="应用名:").pack(side=tk.LEFT)
        v_name = tk.StringVar()
        tk.Entry(f_row, textvariable=v_name, width=10).pack(side=tk.LEFT, padx=5)
        
        tk.Label(f_row, text="进程名(.exe):").pack(side=tk.LEFT)
        v_proc = tk.StringVar()
        tk.Entry(f_row, textvariable=v_proc, width=15).pack(side=tk.LEFT, padx=5)
        
        # 提示信息
        tk.Label(add_frame, text="提示: 进程名必须包含 .exe 后缀,如 chrome.exe", 
                 fg="gray", font=("微软雅黑", 8)).pack(anchor=tk.W, pady=(5, 0))
        
        # Treeview区域
        list_frame = tk.Frame(win, padx=10)
        list_frame.pack(fill=tk.BOTH, expand=True)
        
        columns = ("name", "proc", "status")
        tree = ttk.Treeview(list_frame, columns=columns, show="headings")
        tree.heading("name", text="应用名称")
        tree.heading("proc", text="进程名")
        tree.heading("status", text="状态")
        tree.column("name", width=120)
        tree.column("proc", width=120)
        tree.column("status", width=60)
        
        scroll = ttk.Scrollbar(list_frame, orient=tk.VERTICAL, command=tree.yview)
        tree.configure(yscrollcommand=scroll.set)
        
        tree.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
        scroll.pack(side=tk.RIGHT, fill=tk.Y)
        
        def refresh_list():
            for item in tree.get_children():
                tree.delete(item)
            for k, v in self.cfg.app_blacklist.items():
                status = "已禁用" if v['enabled'] else "未禁用" # 修改状态文本
                tree.insert("", tk.END, values=(v['display_name'], v['process_name'], status), tags=(k,))
        
        refresh_list()
        
        def do_add():
            name = v_name.get().strip()
            proc = v_proc.get().strip()
            if not name or not proc:
                messagebox.showwarning("提示", "请填写完整信息")
                return
            
            key = proc.lower()
            self.cfg.app_blacklist[key] = {
                "enabled": True, "process_name": proc, "display_name": name
            }
            self.cfg.save_blacklist()
            v_name.set("")
            v_proc.set("")
            refresh_list()
            
        tk.Button(f_row, text="添加", command=do_add, bg="#28a745", fg="white").pack(side=tk.LEFT, padx=10)

        # 底部操作栏
        btn_frame = tk.Frame(win, pady=10)
        btn_frame.pack(fill=tk.X)
        
        def toggle_app():
            sel = tree.selection()
            if not sel: return
            key = tree.item(sel[0], "tags")[0]
            self.cfg.app_blacklist[key]['enabled'] = not self.cfg.app_blacklist[key]['enabled']
            self.cfg.save_blacklist()
            refresh_list()
            
        def del_app():
            sel = tree.selection()
            if not sel: return
            key = tree.item(sel[0], "tags")[0]
            if messagebox.askyesno("确认", "删除此规则?"):
                del self.cfg.app_blacklist[key]
                self.cfg.save_blacklist()
                refresh_list()
        
        tk.Button(btn_frame, text="切换 禁用/解禁", command=toggle_app).pack(side=tk.LEFT, padx=20, expand=True)
        tk.Button(btn_frame, text="删除选中规则", command=del_app, fg="red").pack(side=tk.LEFT, padx=20, expand=True)

    def exit_app(self):
        """完全退出 (由X按钮触发)"""
        if not self.verify_password(): return
        
        if messagebox.askyesno("确认", "确定要退出管控吗?\n退出后将不再限制使用时间。"):
            self.sys_ctrl.cancel_shutdown()
            self.root.destroy()
            sys.exit(0)

def main():
    # 隐藏控制台 (Windows Only)
    if platform.system() == "Windows":
        try:
            import ctypes
            ctypes.windll.user32.ShowWindow(ctypes.windll.kernel32.GetConsoleWindow(), 0)
        except:
            pass
            
    root = tk.Tk()
    app = ChildControlApp(root)
    root.mainloop()

if __name__ == "__main__":
    main()

免费评分

参与人数 2吾爱币 +4 热心值 +2 收起 理由
苏紫方璇 + 3 + 1 欢迎分析讨论交流,吾爱破解论坛有你更精彩!
wzg020228 + 1 + 1 谢谢@Thanks!

查看全部评分

本帖被以下淘专辑推荐:

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

联盟少侠 发表于 2026-2-4 01:28
小孩:用AI 搞了一个反定时关机软件,一键清除定时关机
EnterpriseSolu 发表于 2026-2-4 14:56
说句实话,现在小孩子玩电脑,你要引导,打字,练习文档与表格,制作,大型的联机游戏,都是付费的,想玩也玩不了,往街道上一走,小孩子们,都是平板或是andriod手机游戏
Mjudge1116 发表于 2026-2-4 01:42
额行吧,现在小朋友都是手机原住民了,三角洲、和平、王者
willallen 发表于 2026-2-4 00:40
现在还有小孩玩电脑?不都玩手机去了吗?
chenql 发表于 2026-2-4 08:49
家里好几台电脑,我搞了个远程控制的,要玩电脑?玩到什么时候?得跟我说 QQ截图20260204084745.jpg
Ganjiayi 发表于 2026-2-4 09:08
你们家路由没有管控功能吗?直接限制网络啊,网络不通他玩个啥?
saitoloo 发表于 2026-2-4 00:32
好工具,谢谢
cchester 发表于 2026-2-4 02:20
来个手机防沉迷的
yule520184 发表于 2026-2-4 05:28
哈哈哈!这个是真的实用!
xinchenmetx 发表于 2026-2-4 05:36
没进程守护 抬走
picoyiyi 发表于 2026-2-4 07:18
很好的防沉迷!
wish2086 发表于 2026-2-4 07:41
这个牛逼   爱了爱了  
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - 52pojie.cn ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2026-5-3 16:10

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表