import os
import time
import threading
import csv
from datetime import datetime, timedelta
import tkinter as tk
from tkinter import messagebox
from pynput import mouse, keyboard
import pandas as pd
import openpyxl
from openpyxl import Workbook
from openpyxl.styles import Font, PatternFill
from openpyxl.utils import get_column_letter
import sys
import winshell
from win32com.client import Dispatch
import ctypes
import psutil
import win32event
import win32api
import winerror
import json
import hashlib
# 定义基础无操作时间常量
FIRST_LOCK_LIMIT = 5 # 首次锁定的无操作时间(秒)
LOCK_LIMIT = 30 # 后续锁定的无操作时间(秒)
# 添加报表文件保护密码
REPORT_PASSWORD = "123" # 用于保护生成的Excel文件
REPORT_DIR = "D:/Users/x00707/LCZ_Machine_Time/LCZ_Machine_Time/UseTime_Summary/"
REPORT_FILE_PREFIX = "UseTime_Summary_" # 修改为前缀格式
CONFIG_VERSION = "1.6" # 更新版本号
USER_DATA_FILE = "user_data.json"
# 添加默认用户列表(包括两个普通账号)
DEFAULT_USERS = {
"admin": {"password": "123", "role": "admin"},
"001": {"password": "123", "role": "user"},
"002": {"password": "123", "role": "user"},
"102": {"password": "123", "role": "user"}
}
last_activity_time = time.time()
is_locked = False
session_start_time = None
current_date = datetime.now().date()
lock_window = None
app_running = True
daily_usage = {}
first_run = False
current_user = None
lock_count = 0 # 记录本次启动后的锁定次数
mutex_name = "Global\\MachineTimeMonitor"
mutex = win32event.CreateMutex(None, False, mutex_name)
last_error = win32api.GetLastError()
if last_error == winerror.ERROR_ALREADY_EXISTS:
sys.exit(0)
def get_inactivity_limit():
global lock_count
return FIRST_LOCK_LIMIT if lock_count == 0 else LOCK_LIMIT
def hash_password(password):
return hashlib.sha256(password.encode()).hexdigest()
def load_user_data():
"""加载用户数据,自动添加缺失的默认用户"""
if os.path.exists(USER_DATA_FILE):
try:
with open(USER_DATA_FILE, 'r') as f:
user_data = json.load(f)
except:
# 文件损坏时创建默认用户数据
user_data = create_default_user()
else:
# 文件不存在时创建默认用户数据
user_data = create_default_user()
# 检查并添加缺失的默认用户
updated = False
for username, user_info in DEFAULT_USERS.items():
if username not in user_data["users"]:
# 添加缺失的用户
user_data["users"][username] = {
"password": hash_password(user_info["password"]),
"role": user_info["role"],
"first_login": True
}
updated = True
if updated:
save_user_data(user_data) # 保存更新后的用户数据
return user_data
def create_default_user():
"""创建默认用户,使用全局 DEFAULT_USERS"""
default_user = {"users": {}}
for username, user_info in DEFAULT_USERS.items():
default_user["users"][username] = {
"password": hash_password(user_info["password"]),
"role": user_info["role"],
"first_login": True
}
save_user_data(default_user)
return default_user
def save_user_data(user_data):
with open(USER_DATA_FILE, 'w') as f:
json.dump(user_data, f, indent=2)
def add_user(username, password, role="user"):
user_data = load_user_data()
if username in user_data["users"]:
return False, "用户已存在"
user_data["users"][username] = {"password": hash_password(password), "role": role, "first_login": True}
save_user_data(user_data)
return True, "用户添加成功"
def change_password(username, old_password, new_password):
user_data = load_user_data()
if username not in user_data["users"]:
return False, "用户不存在"
user = user_data["users"][username]
if user["password"] != hash_password(old_password):
return False, "原密码不正确"
user["password"] = hash_password(new_password)
user["first_login"] = False
save_user_data(user_data)
return True, "密码修改成功"
def authenticate(username, password):
user_data = load_user_data()
if username not in user_data["users"]:
return False, "用户不存在", None
user = user_data["users"][username]
if user["password"] != hash_password(password):
return False, "密码不正确", None
return True, "登录成功", user["role"]
def create_shortcut():
startup_folder = winshell.startup()
shortcut_path = os.path.join(startup_folder, "MachineTimeMonitor.lnk")
current_script = os.path.abspath(__file__)
if not os.path.exists(shortcut_path) or not is_shortcut_current(shortcut_path, current_script):
target = sys.executable
wDir = os.path.dirname(current_script)
shell = Dispatch('WScript.Shell')
shortcut = shell.CreateShortCut(shortcut_path)
shortcut.Targetpath = target
shortcut.Arguments = f'"{current_script}"'
shortcut.WorkingDirectory = wDir
shortcut.IconLocation = sys.executable
shortcut.Description = f"机台监控 v{CONFIG_VERSION}"
shortcut.save()
def is_shortcut_current(shortcut_path, current_script):
try:
shell = Dispatch('WScript.Shell')
shortcut = shell.CreateShortCut(shortcut_path)
return shortcut.Arguments == f'"{current_script}"'
except:
return False
def terminate_existing_instances():
current_pid = os.getpid()
current_script = os.path.basename(__file__)
for proc in psutil.process_iter(['pid', 'name', 'cmdline']):
try:
if proc.info['name'].lower() in ['python.exe', 'pythonw.exe']:
cmdline = proc.info['cmdline']
if (cmdline and len(cmdline) > 1 and current_script in cmdline[1] and proc.info['pid'] != current_pid):
proc.terminate()
time.sleep(0.5)
except (psutil.NoSuchProcess, psutil.AccessDenied, psutil.ZombieProcess):
pass
def load_historical_data():
"""从 Excel 报表文件加载历史数据"""
global daily_usage
daily_usage.clear()
if not os.path.exists(REPORT_DIR):
return
# 遍历报表目录下的所有 Excel 文件
for filename in os.listdir(REPORT_DIR):
if filename.startswith(REPORT_FILE_PREFIX) and filename.endswith(".xlsx"):
try:
wb = openpyxl.load_workbook(os.path.join(REPORT_DIR, filename))
if "Session Details" in wb.sheetnames:
ws = wb["Session Details"]
# 从第二行开始读取数据(跳过标题行)
for row in range(2, ws.max_row + 1):
date_str = ws.cell(row, 1).value # A列:Date
user = ws.cell(row, 2).value # B列:Account
duration = ws.cell(row, 5).value # E列:Duration(min)
if date_str and user and duration:
user_key = f"{date_str}_{user}"
daily_usage[user_key] = daily_usage.get(user_key, 0) + float(duration)
except Exception as e:
print(f"加载历史数据时出错({filename}): {e}")
def log_session(start_time, end_time, user):
"""记录会话并生成使用时间报表"""
# 处理跨天使用的情况
current_day = start_time.date()
end_day = end_time.date()
if current_day == end_day:
# 记录单日使用情况
duration = (end_time - start_time).total_seconds() / 60
date_str = start_time.strftime("%Y-%m-%d")
start_str = start_time.strftime("%H:%M:%S")
end_str = end_time.strftime("%H:%M:%S")
# 更新每日使用时间
user_key = f"{date_str}_{user}"
daily_usage[user_key] = daily_usage.get(user_key, 0) + duration
# 生成/更新当天的使用时间报表
generate_daily_report(current_day, [
[date_str, user, start_str, end_str, f"{duration:.1f}"]
])
else:
# 处理跨天使用的情况(第一天部分)
day1_end = datetime.combine(start_time.date(), datetime.max.time()).replace(microsecond=0)
duration1 = (day1_end - start_time).total_seconds() / 60
date_str1 = start_time.strftime("%Y-%m-%d")
start_str1 = start_time.strftime("%H:%M:%S")
end_str1 = day1_end.strftime("%H:%M:%S")
# 第二天部分
day2_start = datetime.combine(end_time.date(), datetime.min.time())
duration2 = (end_time - day2_start).total_seconds() / 60
date_str2 = end_time.strftime("%Y-%m-%d")
start_str2 = day2_start.strftime("%H:%M:%S")
end_str2 = end_time.strftime("%H:%M:%S")
# 更新每日使用时间
user_key1 = f"{date_str1}_{user}"
user_key2 = f"{date_str2}_{user}"
daily_usage[user_key1] = daily_usage.get(user_key1, 0) + duration1
daily_usage[user_key2] = daily_usage.get(user_key2, 0) + duration2
# 生成/更新两天的使用时间报表
generate_daily_report(start_time.date(),[[date_str1, user, start_str1, end_str1, f"{duration1:.1f}"]])
generate_daily_report(end_time.date(), [[date_str2, user, start_str2, end_str2, f"{duration2:.1f}"]])
def generate_daily_report(report_date, new_records=None):
"""生成每日使用时间报表,添加 Date 列"""
# 提取年份和月份(格式化为两位数)
year_str = report_date.strftime("%Y")
month_str = report_date.strftime("%m")
# 创建年/月子目录路径
report_path = os.path.join(REPORT_DIR, year_str, month_str)
# 确保年/月目录存在
os.makedirs(report_path, exist_ok=True)
# 创建完整的文件路径
date_str = report_date.strftime("%Y%m%d")
report_filename = os.path.join(report_path, f"{REPORT_FILE_PREFIX}{date_str}.xlsx")
try:
# 如果文件已存在,加载现有工作簿
if os.path.exists(report_filename):
wb = openpyxl.load_workbook(report_filename)
ws1 = wb["Session Details"] if "Session Details" in wb.sheetnames else wb.create_sheet("Session Details")
ws2 = wb["Daily Summary"] if "Daily Summary" in wb.sheetnames else wb.create_sheet("Daily Summary")
else:
# 创建新工作簿
wb = Workbook()
ws1 = wb.active
ws1.title = "Session Details"
# 添加包含 Date 列的表头
ws1.append(['Date', 'Account', 'StartTime', 'EndTime', 'Duration(min)'])
ws2 = wb.create_sheet("Daily Summary")
ws2.append(['Account', 'UseTime(minutes)', 'UseTime(hours)'])
# 添加新记录(如果有)
if new_records:
for record in new_records:
ws1.append(record)
# 处理详细使用记录
user_totals = {}
# 从第二行开始读取现有数据(跳过标题行)
for row in range(2, ws1.max_row + 1):
user = ws1.cell(row, 2).value # B列:Account
duration = ws1.cell(row, 5).value # E列:Duration(min)
if user and duration:
try:
# 确保持续时间是数字
duration_val = float(duration)
user_totals[user] = user_totals.get(user, 0) + duration_val
except (ValueError, TypeError):
pass
# 清除旧汇总数据(保留标题行)
if ws2.max_row > 1:
ws2.delete_rows(2, ws2.max_row - 1)
# 生成每日汇总
for user, total_minutes in user_totals.items():
ws2.append([user, total_minutes, f"{total_minutes / 60:.2f}"])
# 应用样式
for ws in [ws1, ws2]:
for col in ws.columns:
max_length = 0
column_letter = get_column_letter(col[0].column)
for cell in col:
try:
if len(str(cell.value)) > max_length:
max_length = len(str(cell.value))
except:
pass
adjusted_width = (max_length + 2) * 1.2
ws.column_dimensions[column_letter].width = adjusted_width
# 设置标题样式
header_fill = PatternFill(start_color="4F81BD", end_color="4F81BD", fill_type="solid")
header_font = Font(color="FFFFFF", bold=True)
for row in ws.iter_rows(min_row=1, max_row=1):
for cell in row:
cell.fill = header_fill
cell.font = header_font
# 保存报表
# 设置工作簿保护密码
wb.security = openpyxl.workbook.protection.WorkbookProtection(
workbookPassword=REPORT_PASSWORD,
lockStructure=False, # 允许修改工作表结构
lockWindows=False # 允许调整窗口大小
)
wb.security.set_workbook_password(REPORT_PASSWORD)
# 设置每个工作表的保护
for ws in wb.worksheets:
ws.protection.set_password(REPORT_PASSWORD)
ws.protection.sheet = True
ws.protection.enable()
# 保存报表(原有代码)
wb.save(report_filename)
print(f"已更新日报表: {report_filename}")
except Exception as e:
print(f"生成每日报表时出错: {e}")
def show_change_password_window(parent, username):
change_pw_window = tk.Toplevel(parent)
change_pw_window.title("修改密码")
change_pw_window.geometry("400x300")
change_pw_window.resizable(False, False)
change_pw_window.grab_set()
change_pw_window.transient(parent)
change_pw_window.configure(bg="#2c3e50")
window_width = 400
window_height = 300
screen_width = change_pw_window.winfo_screenwidth()
screen_height = change_pw_window.winfo_screenheight()
x = (screen_width - window_width) // 2
y = (screen_height - window_height) // 2
change_pw_window.geometry(f"{window_width}x{window_height}+{x}+{y}")
frame = tk.Frame(change_pw_window, bg="#34495e", padx=20, pady=20)
frame.pack(fill="both", expand=True, padx=20, pady=20)
tk.Label(frame, text=f"修改密码 - {username}", bg="#34495e",
fg="#ecf0f1", font=("Arial", 14, "bold")).grid(row=0, column=0, columnspan=2, pady=(0, 15))
tk.Label(frame, text="原密码:", bg="#34495e", fg="#ecf0f1",
font=("Arial", 12)).grid(row=1, column=0, sticky="e", pady=5)
old_pw_var = tk.StringVar()
old_pw_entry = tk.Entry(frame, textvariable=old_pw_var, show="*", font=("Arial", 12), width=20)
old_pw_entry.grid(row=1, column=1, padx=10, pady=5)
tk.Label(frame, text="新密码:", bg="#34495e", fg="#ecf0f1",
font=("Arial", 12)).grid(row=2, column=0, sticky="e", pady=5)
new_pw_var = tk.StringVar()
new_pw_entry = tk.Entry(frame, textvariable=new_pw_var, show="*", font=("Arial", 12), width=20)
new_pw_entry.grid(row=2, column=1, padx=10, pady=5)
tk.Label(frame, text="确认新密码:", bg="#34495e", fg="#ecf0f1",
font=("Arial", 12)).grid(row=3, column=0, sticky="e", pady=5)
confirm_pw_var = tk.StringVar()
confirm_pw_entry = tk.Entry(frame, textvariable=confirm_pw_var, show="*", font=("Arial", 12), width=20)
confirm_pw_entry.grid(row=3, column=1, padx=10, pady=5)
status_var = tk.StringVar()
status_label = tk.Label(frame, textvariable=status_var, bg="#34495e", fg="#e74c3c", font=("Arial", 10))
status_label.grid(row=4, column=0, columnspan=2, pady=5)
def change_password_action():
old_pw = old_pw_var.get()
new_pw = new_pw_var.get()
confirm_pw = confirm_pw_var.get()
if not old_pw or not new_pw or not confirm_pw:
status_var.set("所有字段都必须填写")
return
if new_pw != confirm_pw:
status_var.set("新密码和确认密码不匹配")
return
success, message = change_password(username, old_pw, new_pw)
if success:
messagebox.showinfo("成功", message, parent=change_pw_window)
change_pw_window.destroy()
else:
status_var.set(message)
button_frame = tk.Frame(frame, bg="#34495e")
button_frame.grid(row=5, column=0, columnspan=2, pady=(10, 0))
change_btn = tk.Button(button_frame, text="修改密码", command=change_password_action, bg="#3498db", fg="white",
font=("Arial", 12), padx=15, pady=5, bd=0)
change_btn.pack(side="left", padx=10)
cancel_btn = tk.Button(button_frame, text="取消", command=change_pw_window.destroy, bg="#95a5a6", fg="white",
font=("Arial", 12), padx=15, pady=5, bd=0)
cancel_btn.pack(side="left", padx=10)
change_pw_window.bind("<Return>", lambda event: change_password_action())
old_pw_entry.focus_set()
def show_lock_window():
global lock_window
lock_window = tk.Tk()
# 添加以下代码确保窗口获得焦点
lock_window.focus_force()
lock_window.grab_set_global() # 捕获所有输入事件
lock_window.after(100, lambda: username_entry.focus_force()) # 延迟100ms确保焦点设置成功
lock_window.title("机台已锁定")
lock_window.attributes("-fullscreen", True)
lock_window.attributes("-topmost", True)
lock_window.configure(bg="#2c3e50")
lock_window.option_add("*Font", "Arial 14")
main_frame = tk.Frame(lock_window, bg="#2c3e50")
main_frame.pack(fill="both", expand=True)
center_frame = tk.Frame(main_frame, bg="#34495e", padx=40, pady=40)
center_frame.place(relx=0.5, rely=0.5, anchor="center")
tk.Label(center_frame, text="机台已锁定", bg="#34495e", fg="#ecf0f1",
font=("Arial", 24, "bold")).grid(row=0, column=0, columnspan=2, pady=(0, 20))
tk.Label(center_frame, text="检测到机台长时间未使用", bg="#34495e", fg="#bdc3c7",
font=("Arial", 14)).grid(row=1, column=0, columnspan=2, pady=(0, 10))
tk.Label(center_frame, text="账号:", bg="#34495e", fg="#ecf0f1",
font=("Arial", 14)).grid(row=2, column=0, padx=5, pady=10, sticky="e")
username_var = tk.StringVar()
username_entry = tk.Entry(center_frame, textvariable=username_var, font=("Arial", 14), width=20)
username_entry.grid(row=2, column=1, padx=5, pady=10, sticky="w")
username_entry.focus()
tk.Label(center_frame, text="密码:", bg="#34495e", fg="#ecf0f1",
font=("Arial", 14)).grid(row=3, column=0, padx=5, pady=10, sticky="e")
password_var = tk.StringVar()
password_entry = tk.Entry(center_frame, textvariable=password_var, show="*", font=("Arial", 14), width=20)
password_entry.grid(row=3, column=1, padx=5, pady=10, sticky="w")
status_var = tk.StringVar()
status_label = tk.Label(center_frame, textvariable=status_var, bg="#34495e", fg="#e74c3c", font=("Arial", 12))
status_label.grid(row=4, column=0, columnspan=2, pady=10)
button_frame = tk.Frame(center_frame, bg="#34495e")
button_frame.grid(row=5, column=0, columnspan=2, pady=(20, 10))
def attempt_unlock():
username = username_var.get()
password = password_var.get()
if not username or not password:
status_var.set("账号和密码不能为空")
return
success, message, role = authenticate(username, password)
if success:
user_data = load_user_data()
if user_data["users"][username]["first_login"]:
show_change_password_window(lock_window, username)
status_var.set("首次登录,请修改密码")
return
unlock(username)
lock_window.destroy()
else:
status_var.set(message)
# 登录失败后重新聚焦到账号输入框
username_entry.focus_force()
def change_password_action():
username = username_var.get()
if not username:
status_var.set("请先输入账号")
return
user_data = load_user_data()
if username not in user_data["users"]:
status_var.set("用户不存在")
return
show_change_password_window(lock_window, username)
login_btn = tk.Button(button_frame, text="登录", command=attempt_unlock, bg="#3498db", fg="white",
font=("Arial", 14), padx=20, pady=5, bd=0)
login_btn.pack(side="left", padx=10)
change_pw_btn = tk.Button(button_frame, text="修改密码", command=change_password_action, bg="#f39c12", fg="white",
font=("Arial", 14), padx=20, pady=5, bd=0)
change_pw_btn.pack(side="left", padx=10)
password_entry.bind("<Return>", lambda event: attempt_unlock())
tk.Label(main_frame, text=f"机台使用时间监控系统 v{CONFIG_VERSION}", bg="#2c3e50", fg="#7f8c8d",
font=("Arial", 10)).pack(side="bottom", pady=10)
lock_window.protocol("WM_DELETE_WINDOW", lambda: None)
lock_window.mainloop()
def unlock(username):
global is_locked, session_start_time, last_activity_time, current_user
is_locked = False
session_start_time = datetime.now()
last_activity_time = time.time()
current_user = username
print(f"用户 {username} 已解锁")
def on_activity():
global last_activity_time
last_activity_time = time.time()
def activity_monitor():
global is_locked, session_start_time, current_date, current_user, lock_count
while app_running:
today = datetime.now().date()
if today != current_date:
# 新的一天开始,生成前一天的报表
generate_daily_report(current_date)
current_date = today
inactivity_limit = get_inactivity_limit()
inactive_time = time.time() - last_activity_time
if inactive_time > inactivity_limit and not is_locked:
lock_count += 1
is_locked = True
if session_start_time and current_user:
end_time = datetime.now()
print(f"记录会话: {current_user} 从 {session_start_time} 到 {end_time}")
log_session(session_start_time, end_time, current_user)
session_start_time = None
current_user = None
show_lock_window()
time.sleep(1)
def start_listeners():
mouse_listener = mouse.Listener(on_move=lambda x, y: on_activity(),
on_click=lambda x, y, button, pressed: on_activity())
keyboard_listener = keyboard.Listener(on_press=lambda key: on_activity())
mouse_listener.start()
keyboard_listener.start()
return mouse_listener, keyboard_listener
def hide_console():
if sys.platform == "win32":
console_window = ctypes.windll.kernel32.GetConsoleWindow()
if console_window:
ctypes.windll.user32.ShowWindow(console_window, 0)
def is_running_as_background():
try:
parent_pid = psutil.Process(os.getpid()).ppid()
parent_name = psutil.Process(parent_pid).name().lower()
return parent_name == "explorer.exe"
except:
return False
def main():
global session_start_time, app_running, first_run, lock_count
terminate_existing_instances()
if not ctypes.windll.shell32.IsUserAnAdmin():
ctypes.windll.shell32.ShellExecuteW(None, "runas", sys.executable, f'"{__file__}"', None, 1)
sys.exit()
hide_console()
create_shortcut()
os.makedirs(REPORT_DIR, exist_ok=True)
load_historical_data()
first_run = not any(
f.startswith(REPORT_FILE_PREFIX) and f.endswith(".xlsx")
for f in os.listdir(REPORT_DIR)
)
monitor_thread = threading.Thread(target=activity_monitor, daemon=True)
monitor_thread.start()
mouse_listener, keyboard_listener = start_listeners()
session_start_time = datetime.now()
if first_run:
root = tk.Tk()
root.withdraw()
messagebox.showinfo("启动成功", "机台使用时间监控系统已成功启动!")
root.destroy()
try:
import pystray
from PIL import Image
def exit_action(icon, item):
global app_running
app_running = False
icon.stop()
sys.exit()
def show_info():
root = tk.Tk()
root.withdraw()
messagebox.showinfo("程序信息", f"机台使用时间监控系统 v{CONFIG_VERSION}\n"
f"首次锁定时间: {FIRST_LOCK_LIMIT}秒\n"
f"后续锁定时间: {LOCK_LIMIT}秒")
root.destroy()
image = Image.new('RGB', (64, 64), color='white')
menu = pystray.Menu(
pystray.MenuItem("程序信息", show_info),
pystray.MenuItem("退出", exit_action)
)
icon = pystray.Icon("machine_monitor", image, "机台使用监控", menu)
icon.run_detached()
except ImportError:
if not is_running_as_background() and not hasattr(sys, 'tray_warning_shown'):
root = tk.Tk()
root.withdraw()
messagebox.showinfo("后台运行", "机台监控程序已在后台运行")
root.destroy()
sys.tray_warning_shown = True
try:
while app_running:
time.sleep(1)
except KeyboardInterrupt:
app_running = False
finally:
if session_start_time and not is_locked and current_user:
log_session(session_start_time, datetime.now(), current_user)
mouse_listener.stop()
keyboard_listener.stop()
if __name__ == "__main__":
main()
上面的test4.py原代码代码是可以初次运行后一直在后台运行的成功监控键盘和鼠标状态进行锁定,然后登录允许的账号和密码解锁,以及修改密码的功能,电脑关机重启后,开机自动开始运行后台监控,根据锁定的次数判断等待时间。
如果我想要写一个专门用于窗口的另一个python代码(在同一个工程里面),这个窗口代码收集到的信息会关联到上面的这个test4.py代码的参数。下面是我想要实现的窗口代码的功能:
①在桌面创建快捷方式——(打开之后有个窗口可以登录管理员的账号,就像原代码锁定界面登录的管理员账号密码一样)——登录管理员账号后可以在窗口里修改一些原test4.py代码的参数(修改什么参数在后面几点我会说明)。然后这个快捷方式的窗口上面(也就是登录管理员账号的上面部分)也可以选择退出登录也就是重新进入锁定(不管是管理员账号还是普通账号)。
②快捷方式的窗口,上面中间部分是选择退出当前登录按键;下面部分是登录管理员账号,可以输入账号和密码,登录后一个新的管理员窗口包含:一个可以修改锁定时间的输入框,一个修改保存的excel的保护密码的输入框,一个选择退出登录管理员账号的按键(右上角),一个可以滑动的子框可以添加多个管理员账号,一个可以滑动的子框可以添加允许登录的普通账号,一个可以重新在cmd里-instal运行修改参数后的原代码的按键,一个确定上面修改的按键,七个部分从上往下依次排。
③关联原代码参数的说明:在窗口中实时修改原代码的参数(原代码的文件名是test4.py),参数包含:1.选择退出登录按键关联原代码进入锁定界面重新登录;2.管理员账号窗口:2.1修改锁定时间的输入框关联原代码的LOCK_LIMIT (但是这里输入的单位是分钟)。 2.2退出登录管理员账号的按键就返回到快捷方式的窗口。 2.3子框可以添加多个管理员账号和普通账号关联到原代码的user_data允许在锁定界面登录的账号。 2.4修改保存的excel的保护密码的输入框关联到原代码的REPORT_PASSWORD。 2.5重新在cmd里-instal运行修改参数后的原代码的按键关联到原代码修改之后的重新运行(先结束任务后-install运行)
④或者说,这个新的窗口代码关联到原代码的一个配置文件,这样已经在后台运行的原代码就更实时更新到修改的参数呢?
请给我完整的窗口代码的python代码,如果不能这样实现的话请参考②中2.5所说的重新运行按键
原代码只用告诉我需要修改的地方,不用告诉我完整代码了
注意:其他我没叫你修改的地方的原代码不做修改(包括格式)(不改变其他代码的功能!特别是开机自动启动后台运行),代码尽量简洁,逻辑不复杂,适当的地方添加注释便于看懂
下面这个代码是我尝试实现上述功能的代码,虽然窗口差不多是我预设的内容(还需要你稍微修改,添加修改保存的excel的保护密码的输入框),但是不能和正在后台运行的test4.py代码及时联动,退出登录按键按了没反应,管理员修改参数的界面:添加用户显示用户不存在,添加不了,修改等待时间也不成功
import os
import sys
import json
import tkinter as tk
from tkinter import ttk, messagebox, simpledialog
import subprocess
import threading
import ctypes
from datetime import datetime
import psutil
import winshell
from win32com.client import Dispatch
# 配置文件路径
CONFIG_FILE = "monitor_config.json"
USER_DATA_FILE = "user_data.json"
# 默认配置
DEFAULT_CONFIG = {
"lock_limit_minutes": 0.5, # 30秒 = 0.5分钟
"admin_users": ["admin"],
"regular_users": ["001", "002", "102"],
"lock_immediately": False
}
class AdminTool:
def __init__(self):
self.root = tk.Tk()
self.root.title("机台监控管理工具")
self.root.geometry("600x500")
self.root.resizable(True, True)
self.root.protocol("WM_DELETE_WINDOW", self.on_closing)
# 加载配置
self.config = self.load_config()
# 创建主界面
self.create_login_frame()
# 创建桌面快捷方式
self.create_desktop_shortcut()
self.root.mainloop()
def load_config(self):
"""加载配置文件"""
if os.path.exists(CONFIG_FILE):
try:
with open(CONFIG_FILE, 'r') as f:
return json.load(f)
except:
# 如果文件损坏,创建默认配置
self.save_config(DEFAULT_CONFIG)
return DEFAULT_CONFIG.copy()
# 如果文件不存在,创建默认配置
self.save_config(DEFAULT_CONFIG)
return DEFAULT_CONFIG.copy()
def save_config(self, config):
"""保存配置文件"""
with open(CONFIG_FILE, 'w') as f:
json.dump(config, f, indent=2)
def create_desktop_shortcut(self):
"""在桌面创建快捷方式"""
desktop = winshell.desktop()
shortcut_path = os.path.join(desktop, "机台监控管理工具.lnk")
# 获取当前脚本路径
if getattr(sys, 'frozen', False):
# 打包后的可执行文件
target = sys.executable
else:
# 脚本模式
target = sys.executable
script_path = os.path.abspath(__file__)
if not os.path.exists(shortcut_path):
shell = Dispatch('WScript.Shell')
shortcut = shell.CreateShortCut(shortcut_path)
shortcut.Targetpath = target
if not getattr(sys, 'frozen', False):
shortcut.Arguments = f'"{script_path}"'
shortcut.WorkingDirectory = os.path.dirname(os.path.abspath(__file__))
shortcut.IconLocation = target
shortcut.Description = "机台监控管理工具"
shortcut.save()
def create_login_frame(self):
"""创建登录界面"""
self.clear_window()
# 顶部退出登录按钮
logout_frame = tk.Frame(self.root, bg="#f0f0f0", height=40)
logout_frame.pack(fill=tk.X, side=tk.TOP)
logout_btn = tk.Button(
logout_frame, text="退出登录",
command=self.lock_system,
bg="#e74c3c", fg="white", font=("Arial", 10)
)
logout_btn.pack(side=tk.RIGHT, padx=10, pady=5)
# 登录表单
login_frame = tk.Frame(self.root, padx=20, pady=20)
login_frame.pack(fill=tk.BOTH, expand=True)
tk.Label(
login_frame, text="管理员登录",
font=("Arial", 16, "bold")
).pack(pady=(10, 20))
form_frame = tk.Frame(login_frame)
form_frame.pack(fill=tk.X, padx=50)
tk.Label(form_frame, text="账号:", font=("Arial", 12)).grid(row=0, column=0, sticky="e", pady=5)
self.username_var = tk.StringVar()
username_entry = tk.Entry(form_frame, textvariable=self.username_var, font=("Arial", 12))
username_entry.grid(row=0, column=1, padx=5, pady=5, sticky="ew")
tk.Label(form_frame, text="密码:", font=("Arial", 12)).grid(row=1, column=0, sticky="e", pady=5)
self.password_var = tk.StringVar()
password_entry = tk.Entry(form_frame, textvariable=self.password_var, show="*", font=("Arial", 12))
password_entry.grid(row=1, column=1, padx=5, pady=5, sticky="ew")
form_frame.columnconfigure(1, weight=1)
status_frame = tk.Frame(login_frame)
status_frame.pack(fill=tk.X, pady=10)
self.login_status = tk.Label(status_frame, text="", fg="red")
self.login_status.pack()
btn_frame = tk.Frame(login_frame)
btn_frame.pack(pady=10)
login_btn = tk.Button(
btn_frame, text="登录",
command=self.authenticate_admin,
width=15, bg="#3498db", fg="white", font=("Arial", 12)
)
login_btn.pack(pady=10)
# 绑定回车键
password_entry.bind("<Return>", lambda event: self.authenticate_admin())
username_entry.focus()
def authenticate_admin(self):
"""验证管理员身份"""
username = self.username_var.get().strip()
password = self.password_var.get().strip()
if not username or not password:
self.login_status.config(text="账号和密码不能为空")
return
# 加载用户数据
if not os.path.exists(USER_DATA_FILE):
self.login_status.config(text="用户数据文件不存在")
return
try:
with open(USER_DATA_FILE, 'r') as f:
user_data = json.load(f)
except:
self.login_status.config(text="无法读取用户数据")
return
# 检查用户是否存在
if username not in user_data.get("users", {}):
self.login_status.config(text="用户不存在")
return
user = user_data["users"][username]
# 验证密码
import hashlib
hashed_password = hashlib.sha256(password.encode()).hexdigest()
if user["password"] != hashed_password:
self.login_status.config(text="密码不正确")
return
# 检查是否为管理员
if user.get("role") != "admin":
self.login_status.config(text="该账号不是管理员")
return
# 登录成功,进入管理界面
self.create_admin_panel()
def lock_system(self):
"""锁定系统"""
# 更新配置文件设置立即锁定标志
self.config["lock_immediately"] = True
self.save_config(self.config)
# 创建锁定标志文件
with open("lock_now.flag", "w") as f:
f.write(str(datetime.now()))
messagebox.showinfo("系统锁定", "系统将在后台监控程序中锁定")
# 退出管理员界面
self.create_login_frame()
def create_admin_panel(self):
"""创建管理员面板"""
self.clear_window()
# 创建主容器
main_container = tk.Frame(self.root)
main_container.pack(fill=tk.BOTH, expand=True, padx=10, pady=10)
# 顶部标题和退出按钮
header_frame = tk.Frame(main_container, bg="#34495e", height=40)
header_frame.pack(fill=tk.X, side=tk.TOP, pady=(0, 10))
tk.Label(
header_frame, text="机台监控管理面板",
fg="white", bg="#34495e", font=("Arial", 14, "bold")
).pack(side=tk.LEFT, padx=20)
logout_btn = tk.Button(
header_frame, text="退出管理员",
command=self.create_login_frame,
bg="#e74c3c", fg="white", font=("Arial", 10)
)
logout_btn.pack(side=tk.RIGHT, padx=10, pady=5)
# 锁定时间设置
lock_frame = tk.LabelFrame(main_container, text="锁定时间设置", padx=10, pady=10)
lock_frame.pack(fill=tk.X, pady=5)
lock_setting_frame = tk.Frame(lock_frame)
lock_setting_frame.pack(fill=tk.X, padx=5, pady=5)
tk.Label(
lock_setting_frame,
text="无操作锁定时间(分钟):",
font=("Arial", 11)
).pack(side=tk.LEFT, padx=5)
self.lock_time_var = tk.DoubleVar(value=self.config["lock_limit_minutes"])
lock_entry = tk.Entry(
lock_setting_frame, textvariable=self.lock_time_var,
width=8, font=("Arial", 11)
)
lock_entry.pack(side=tk.LEFT, padx=5)
save_btn = tk.Button(
lock_setting_frame, text="保存设置",
command=self.save_lock_time,
bg="#27ae60", fg="white", width=10
)
save_btn.pack(side=tk.LEFT, padx=10)
# 用户管理容器
user_container = tk.Frame(main_container)
user_container.pack(fill=tk.BOTH, expand=True, pady=5)
# 管理员用户列表
admin_frame = tk.LabelFrame(user_container, text="管理员账号", padx=10, pady=10)
admin_frame.pack(side=tk.LEFT, fill=tk.BOTH, expand=True, padx=(0, 5))
admin_list_frame = tk.Frame(admin_frame)
admin_list_frame.pack(fill=tk.BOTH, expand=True)
# 滚动条
admin_scroll = tk.Scrollbar(admin_list_frame)
admin_scroll.pack(side=tk.RIGHT, fill=tk.Y)
self.admin_listbox = tk.Listbox(
admin_list_frame,
yscrollcommand=admin_scroll.set,
height=6
)
self.admin_listbox.pack(fill=tk.BOTH, expand=True)
admin_scroll.config(command=self.admin_listbox.yview)
# 加载管理员用户
self.load_admin_users()
# 管理员操作按钮
admin_btn_frame = tk.Frame(admin_frame)
admin_btn_frame.pack(fill=tk.X, pady=(5, 0))
add_admin_btn = tk.Button(
admin_btn_frame, text="添加",
command=self.add_admin_user,
width=8
)
add_admin_btn.pack(side=tk.LEFT, padx=2)
remove_admin_btn = tk.Button(
admin_btn_frame, text="移除",
command=self.remove_admin_user,
width=8
)
remove_admin_btn.pack(side=tk.LEFT, padx=2)
# 普通用户列表
user_frame = tk.LabelFrame(user_container, text="普通用户账号", padx=10, pady=10)
user_frame.pack(side=tk.RIGHT, fill=tk.BOTH, expand=True, padx=(5, 0))
user_list_frame = tk.Frame(user_frame)
user_list_frame.pack(fill=tk.BOTH, expand=True)
# 滚动条
user_scroll = tk.Scrollbar(user_list_frame)
user_scroll.pack(side=tk.RIGHT, fill=tk.Y)
self.user_listbox = tk.Listbox(
user_list_frame,
yscrollcommand=user_scroll.set,
height=6
)
self.user_listbox.pack(fill=tk.BOTH, expand=True)
user_scroll.config(command=self.user_listbox.yview)
# 加载普通用户
self.load_regular_users()
# 普通用户操作按钮
user_btn_frame = tk.Frame(user_frame)
user_btn_frame.pack(fill=tk.X, pady=(5, 0))
add_user_btn = tk.Button(
user_btn_frame, text="添加",
command=self.add_regular_user,
width=8
)
add_user_btn.pack(side=tk.LEFT, padx=2)
remove_user_btn = tk.Button(
user_btn_frame, text="移除",
command=self.remove_regular_user,
width=8
)
remove_user_btn.pack(side=tk.LEFT, padx=2)
# 重启服务按钮
restart_frame = tk.Frame(main_container)
restart_frame.pack(fill=tk.X, pady=10)
restart_btn = tk.Button(
restart_frame, text="重启监控服务",
command=self.restart_monitor_service,
bg="#e67e22", fg="white", font=("Arial", 12), height=2
)
restart_btn.pack(fill=tk.X, padx=20)
def load_admin_users(self):
"""加载管理员用户到列表"""
self.admin_listbox.delete(0, tk.END)
for user in self.config["admin_users"]:
self.admin_listbox.insert(tk.END, user)
def load_regular_users(self):
"""加载普通用户到列表"""
self.user_listbox.delete(0, tk.END)
for user in self.config["regular_users"]:
self.user_listbox.insert(tk.END, user)
def save_lock_time(self):
"""保存锁定时间设置"""
try:
lock_time = float(self.lock_time_var.get())
if lock_time <= 0:
raise ValueError("锁定时间必须大于0")
# 更新配置
self.config["lock_limit_minutes"] = lock_time
self.save_config(self.config)
messagebox.showinfo("成功", "锁定时间设置已保存")
except ValueError as e:
messagebox.showerror("错误", f"无效的输入: {str(e)}")
def add_admin_user(self):
"""添加管理员用户"""
username = simpledialog.askstring("添加管理员", "请输入管理员账号:")
if username:
if username in self.config["admin_users"]:
messagebox.showerror("错误", "该用户已是管理员")
return
# 检查用户是否存在
if not os.path.exists(USER_DATA_FILE):
messagebox.showerror("错误", "用户数据文件不存在")
return
try:
with open(USER_DATA_FILE, 'r') as f:
user_data = json.load(f)
except:
messagebox.showerror("错误", "无法读取用户数据")
return
if username not in user_data.get("users", {}):
messagebox.showerror("错误", "该用户不存在")
return
# 添加到管理员列表
self.config["admin_users"].append(username)
self.save_config(self.config)
self.load_admin_users()
def remove_admin_user(self):
"""移除管理员用户"""
selection = self.admin_listbox.curselection()
if not selection:
messagebox.showerror("错误", "请选择要移除的管理员")
return
username = self.admin_listbox.get(selection[0])
if username == "admin":
messagebox.showerror("错误", "不能移除默认管理员账号")
return
self.config["admin_users"].remove(username)
self.save_config(self.config)
self.load_admin_users()
def add_regular_user(self):
"""添加普通用户"""
username = simpledialog.askstring("添加普通用户", "请输入普通用户账号:")
if username:
if username in self.config["regular_users"]:
messagebox.showerror("错误", "该用户已是普通用户")
return
# 检查用户是否存在
if not os.path.exists(USER_DATA_FILE):
messagebox.showerror("错误", "用户数据文件不存在")
return
try:
with open(USER_DATA_FILE, 'r') as f:
user_data = json.load(f)
except:
messagebox.showerror("错误", "无法读取用户数据")
return
if username not in user_data.get("users", {}):
messagebox.showerror("错误", "该用户不存在")
return
# 添加到普通用户列表
self.config["regular_users"].append(username)
self.save_config(self.config)
self.load_regular_users()
def remove_regular_user(self):
"""移除普通用户"""
selection = self.user_listbox.curselection()
if not selection:
messagebox.showerror("错误", "请选择要移除的用户")
return
username = self.user_listbox.get(selection[0])
self.config["regular_users"].remove(username)
self.save_config(self.config)
self.load_regular_users()
def restart_monitor_service(self):
"""重启监控服务"""
# 查找并终止现有监控进程
current_pid = os.getpid()
script_name = "test4.py" # 原代码文件名
terminated = False
for proc in psutil.process_iter(['pid', 'name', 'cmdline']):
try:
cmdline = proc.info['cmdline']
if cmdline and len(cmdline) > 1 and script_name in cmdline[1] and proc.info['pid'] != current_pid:
proc.terminate()
terminated = True
except (psutil.NoSuchProcess, psutil.AccessDenied, psutil.ZombieProcess):
pass
# 稍等片刻让进程终止
if terminated:
import time
time.sleep(1)
# 重新启动监控服务
script_path = os.path.abspath(script_name)
if os.path.exists(script_path):
# 根据运行环境决定如何启动
if getattr(sys, 'frozen', False):
# 打包后的可执行文件
subprocess.Popen([sys.executable, script_path])
else:
# 脚本模式
subprocess.Popen([sys.executable, script_path])
messagebox.showinfo("成功", "监控服务已重启")
else:
messagebox.showerror("错误", f"找不到文件: {script_name}")
def clear_window(self):
"""清除窗口中的所有组件"""
for widget in self.root.winfo_children():
widget.destroy()
def on_closing(self):
"""关闭窗口时的处理"""
self.root.destroy()
if __name__ == "__main__":
# 检查是否以管理员权限运行
if ctypes.windll.shell32.IsUserAnAdmin() == 0:
# 重新以管理员权限运行
ctypes.windll.shell32.ShellExecuteW(None, "runas", sys.executable, f'"{__file__}"', None, 1)
sys.exit()
app = AdminTool()
最新发布