用python语言写一个工具用来监控某个文件夹文件内变化

1.文件内文件发生变化,则通知

2.通过指定窗口发生通知,通知内容为发生改变的文件名,增加文件或者删除等

3.指定窗口可以是QQ或者微信窗口

4.正式开始前,需要测试能否正确成功发送(字符串),因为可能发送时窗口大小不一致,或者无法前置窗口导致无法正确输入字符串。

上代码:

import os
import time
import random  # 用于随机延迟
import json
import hashlib
import ctypes
import threading
import win32gui
import win32con
import win32api
import tkinter as tk
from tkinter import filedialog, messagebox, ttk

CONFIG_FILE = "folder_monitor_config.json"

class Config:
    def __init__(self):
        self.interval = 5  # 默认5秒
        self.folder_path = ""
        self.target_window = "我的手机"
        self.load_config()
    
    def load_config(self):
        if os.path.exists(CONFIG_FILE):
            try:
                with open(CONFIG_FILE, 'r') as f:
                    data = json.load(f)
                    self.interval = data.get('interval', 5)
                    self.folder_path = data.get('folder_path', "")
                    self.target_window = data.get('target_window', "我的手机")
            except:
                # 如果配置文件损坏,使用默认值
                self.interval = 5
                self.folder_path = ""
                self.target_window = "我的手机"
    
    def save_config(self):
        data = {
            'interval': self.interval,
            'folder_path': self.folder_path,
            'target_window': self.target_window
        }
        with open(CONFIG_FILE, 'w') as f:
            json.dump(data, f)

class WindowSender:
    def __init__(self):
        self.user32 = ctypes.windll.user32
        self.KEYEVENTF_KEYUP = 0x0002
    def force_foreground(self, hwnd):
        """强制将窗口置于前台(改进版)"""
        try:
            # 方法1:先尝试常规方式
            win32gui.ShowWindow(hwnd, win32con.SW_RESTORE)
            win32gui.SetForegroundWindow(hwnd)
            time.sleep(0.1)
            
            # 检查是否成功
            if hwnd == win32gui.GetForegroundWindow():
                return True
            
            # 方法2:如果常规方式失败,使用备用方法
            # 保存当前焦点窗口
            foreground = win32gui.GetForegroundWindow()
            
            # 魔法参数:使得SetForegroundWindow可以工作
            self.user32.LockSetForegroundWindow(win32con.LSFW_UNLOCK)
            
            # 将目标窗口设为最顶层
            win32gui.SetWindowPos(
                hwnd, win32con.HWND_TOPMOST, 
                0, 0, 0, 0,
                win32con.SWP_NOMOVE | win32con.SWP_NOSIZE
            )
            win32gui.SetWindowPos(
                hwnd, win32con.HWND_NOTOPMOST, 
                0, 0, 0, 0,
                win32con.SWP_NOMOVE | win32con.SWP_NOSIZE
            )
            
            # 再次尝试获取焦点
            win32gui.SetForegroundWindow(hwnd)
            time.sleep(0.1)
            
            # 恢复原焦点窗口
            if foreground and foreground != hwnd:
                win32gui.SetForegroundWindow(foreground)
                win32gui.SetForegroundWindow(hwnd)
            
            return hwnd == win32gui.GetForegroundWindow()
        except Exception as e:
            print(f"强制置前失败: {e}")
            return False
        
    def send_targeted_input(self, hwnd, text):
        """精准发送到指定窗口"""
        try:
            # 1. 获取目标窗口的编辑控件
            edit_hwnd = win32gui.FindWindowEx(hwnd, None, "Edit", None)
            if not edit_hwnd:
                # 如果找不到标准Edit控件,尝试直接发送到窗口
                edit_hwnd = hwnd
            
            # 2. 激活目标控件
            win32gui.SetForegroundWindow(hwnd)
            win32gui.SetFocus(edit_hwnd)
            time.sleep(0.1)
            
            # 3. 清空原有内容
            win32gui.SendMessage(edit_hwnd, win32con.WM_SETTEXT, 0, "")
            
            # 4. 精准发送字符
            for char in text:
                win32gui.SendMessage(
                    edit_hwnd,
                    win32con.WM_CHAR,
                    ord(char),
                    0
                )
                time.sleep(0.01)
            
            # 5. 发送回车
            win32gui.SendMessage(
                edit_hwnd,
                win32con.WM_KEYDOWN,
                win32con.VK_RETURN,
                0
            )
            win32gui.SendMessage(
                edit_hwnd,
                win32con.WM_KEYUP,
                win32con.VK_RETURN,
                0
            )
            return True
        except Exception as e:
            print(f"定向发送失败: {e}")
            return False

    def send_to_window(self, window_title, text):
        """改进版:只发送到目标窗口"""

        hwnd = win32gui.FindWindow(None, window_title)
        if not hwnd:
            return False
        

        # 先尝试精准发送到编辑框
        if self.send_targeted_input(hwnd, text):
            return True
        
        # 如果失败,尝试备用方法(但限制在目标窗口内)
        try:
            self.force_foreground(hwnd)
            time.sleep(0.2)
            
            # 获取窗口位置
            rect = win32gui.GetWindowRect(hwnd)
            x = (rect[0] + rect[2]) // 2
            y = (rect[1] + rect[3]) // 2
            
            x = x -200
            y = y +200
            
            # 模拟点击确保焦点
            win32api.SetCursorPos((x, y))
            win32api.mouse_event(win32con.MOUSEEVENTF_LEFTDOWN, x, y, 0, 0)
            win32api.mouse_event(win32con.MOUSEEVENTF_LEFTUP, x, y, 0, 0)
            time.sleep(0.1)
            
            for char in text:
                win32api.SendMessage(hwnd, win32con.WM_CHAR, ord(char), 0)
                time.sleep(random.uniform(0.03, 0.08))

            # 发送回车
            time.sleep(0.1)
            win32api.keybd_event(win32con.VK_RETURN, 0, 0, 0)
            win32api.keybd_event(win32con.VK_RETURN, 0, win32con.KEYEVENTF_KEYUP, 0)
            
            return True
        except Exception as e:
            print(f"备用发送方法失败: {e}")
            return False

class FolderMonitor:
    def __init__(self, folder_path, interval, callback, target_window):
        self.folder_path = folder_path
        self.interval = interval
        self.callback = callback
        self.target_window = target_window
        self.window_sender = WindowSender()
        self.last_state = {}
        self.running = False
    
    def get_file_hash(self, filepath):
        """计算文件的哈希值"""
        hasher = hashlib.md5()
        with open(filepath, 'rb') as f:
            buf = f.read()
            hasher.update(buf)
        return hasher.hexdigest()
    
    def scan_folder(self):
        """扫描文件夹并返回文件状态"""
        current_state = {}
        for root, dirs, files in os.walk(self.folder_path):
            for file in files:
                filepath = os.path.join(root, file)
                try:
                    file_stat = os.stat(filepath)
                    current_state[filepath] = {
                        'size': file_stat.st_size,
                        'mtime': file_stat.st_mtime,
                    }
                except:
                    continue
        return current_state
    
    def compare_states(self, old_state, new_state):
        """比较两个状态,返回变化"""
        changes = {
            'added': [],
            'deleted': [],
            'modified': []
        }
        
        # 检查新增和修改的文件
        for filepath in new_state:
            if filepath not in old_state:
                changes['added'].append(filepath)
            else:
                if (new_state[filepath]['size'] != old_state[filepath]['size'] or 
                    new_state[filepath]['mtime'] != old_state[filepath]['mtime']):
                    changes['modified'].append(filepath)
        
        # 检查删除的文件
        for filepath in old_state:
            if filepath not in new_state:
                changes['deleted'].append(filepath)
        
        return changes
    
    def send_changes_to_window(self, changes):
        """将变化发送到目标窗口"""
        if not self.target_window:
            return False
        
        # 准备要发送的消息
        message_lines = []
        if changes['added']:
            message_lines.append("【新增文件】")
            message_lines.extend([f"+ {os.path.basename(f)}" for f in changes['added']])
        if changes['modified']:
            message_lines.append("【修改文件】")
            message_lines.extend([f"* {os.path.basename(f)}" for f in changes['modified']])
        if changes['deleted']:
            message_lines.append("【删除文件】")
            message_lines.extend([f"- {os.path.basename(f)}" for f in changes['deleted']])
        
        if not message_lines:
            return False
        
        message = "\n".join(message_lines)
        return self.window_sender.send_to_window(self.target_window, message)
    
    def start(self):
        """开始监控"""
        self.running = True
        self.last_state = self.scan_folder()
        print(f"开始监控文件夹: {self.folder_path}, 检测间隔: {self.interval}秒")
        
        while self.running:
            time.sleep(self.interval)
            current_state = self.scan_folder()
            changes = self.compare_states(self.last_state, current_state)
            
            if any(changes.values()):
                send_result = self.send_changes_to_window(changes)
                self.callback(changes, send_result)
            
            self.last_state = current_state
    
    def stop(self):
        """停止监控"""
        self.running = False

class FolderMonitorApp:
    def __init__(self, root):
        self.root = root
        self.root.title("文件夹监控与自动发送工具")
        self.config = Config()
        self.monitor = None
        
        # 创建UI
        self.create_ui()
        
        # 加载保存的配置
        self.load_settings()
    
    def create_ui(self):
        """创建用户界面"""
        # 主框架
        main_frame = ttk.Frame(self.root, padding=10)
        main_frame.pack(fill=tk.BOTH, expand=True)
        
        # 文件夹选择部分
        folder_frame = ttk.LabelFrame(main_frame, text="监控文件夹", padding=10)
        folder_frame.pack(fill=tk.X, pady=5)
        
        self.folder_var = tk.StringVar()
        folder_entry = ttk.Entry(folder_frame, textvariable=self.folder_var, width=50)
        folder_entry.pack(side=tk.LEFT, fill=tk.X, expand=True)
        ttk.Button(folder_frame, text="浏览...", command=self.browse_folder).pack(side=tk.LEFT, padx=5)
        
        # 目标窗口设置部分 - 修改为水平布局
        target_frame = ttk.LabelFrame(main_frame, text="目标窗口设置", padding=10)
        target_frame.pack(fill=tk.X, pady=5)
        
        self.target_var = tk.StringVar()
        
        # 使用Frame将输入框和按钮放在同一行
        target_row = ttk.Frame(target_frame)
        target_row.pack(fill=tk.X)
        
        ttk.Label(target_row, text="窗口标题:").pack(side=tk.LEFT, padx=(0,5))
        ttk.Entry(target_row, textvariable=self.target_var, width=40).pack(side=tk.LEFT, fill=tk.X, expand=True)
        ttk.Button(target_row, text="测试窗口", command=self.test_window).pack(side=tk.LEFT, padx=(5,0))
        
        # 间隔时间设置部分
        interval_frame = ttk.LabelFrame(main_frame, text="检测间隔 (1-30秒)", padding=10)
        interval_frame.pack(fill=tk.X, pady=5)
        
        self.interval_var = tk.IntVar()
        ttk.Scale(interval_frame, from_=1, to=30, variable=self.interval_var, 
                 command=lambda v: self.interval_var.set(round(float(v)))).pack(fill=tk.X)
        ttk.Label(interval_frame, textvariable=self.interval_var).pack()
        
        # 控制按钮
        button_frame = ttk.Frame(main_frame, padding=10)
        button_frame.pack(fill=tk.X)
        
        self.start_button = ttk.Button(button_frame, text="开始监控", command=self.threaded_start_monitoring)
        self.start_button.pack(side=tk.LEFT, padx=5)
        
        self.stop_button = ttk.Button(button_frame, text="停止监控", command=self.stop_monitoring, state=tk.DISABLED)
        self.stop_button.pack(side=tk.LEFT, padx=5)
        
        ttk.Button(button_frame, text="保存设置", command=self.save_settings).pack(side=tk.RIGHT)
        ttk.Button(button_frame, text="清空日志", command=self.clear_log).pack(side=tk.RIGHT, padx=5)

        # 日志输出
        log_frame = ttk.LabelFrame(main_frame, text="监控日志", padding=10)
        log_frame.pack(fill=tk.BOTH, expand=True)
        
        self.log_text = tk.Text(log_frame, height=10, state=tk.DISABLED, wrap=tk.WORD)
        scrollbar = ttk.Scrollbar(log_frame, command=self.log_text.yview)
        self.log_text.config(yscrollcommand=scrollbar.set)
        
        self.log_text.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
        scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
        
        
    
    def browse_folder(self):
        """浏览文件夹"""
        folder_path = filedialog.askdirectory()
        if folder_path:
            self.folder_var.set(folder_path)
    
    def test_window(self):
        """测试目标窗口(线程化)"""
        def test_window_thread():
            self.start_button.config(state=tk.DISABLED)
            self.stop_button.config(state=tk.DISABLED)
            
            target_window = self.target_var.get()
            if not target_window:
                messagebox.showerror("错误", "请输入目标窗口标题")
                self.start_button.config(state=tk.NORMAL)
                return
            
            sender = WindowSender()
            hwnd = win32gui.FindWindow(None, target_window)
            
            if hwnd:
                try:
                    sender.force_foreground(hwnd)
                    self.log(f"成功找到并激活窗口: {target_window}")
                    if sender.send_to_window(target_window, "测试消息-文件夹监控工具"):
                        self.log("测试消息发送成功!")
                    else:
                        self.log("窗口找到但消息发送失败")
                except Exception as e:
                    self.log(f"窗口测试失败: {str(e)}")
            else:
                self.log(f"未找到窗口: {target_window}")
            
            self.start_button.config(state=tk.NORMAL)
        
        # 启动线程执行测试
        threading.Thread(target=test_window_thread, daemon=True).start()
    
    def threaded_start_monitoring(self):
        """线程化的开始监控"""
        def start_monitoring_thread():
            self.start_button.config(state=tk.DISABLED)
            self.stop_button.config(state=tk.DISABLED)
            
            folder_path = self.folder_var.get()
            interval = self.interval_var.get()
            target_window = self.target_var.get()
            
            if not folder_path:
                messagebox.showerror("错误", "请先选择要监控的文件夹")
                self.start_button.config(state=tk.NORMAL)
                return
            
            if not os.path.exists(folder_path):
                messagebox.showerror("错误", "指定的文件夹不存在")
                self.start_button.config(state=tk.NORMAL)
                return
            
            if interval < 1 or interval > 30:
                messagebox.showerror("错误", "检测间隔必须在1-30秒之间")
                self.start_button.config(state=tk.NORMAL)
                return
            
            # 保存当前设置
            self.save_settings()
            
            # 启动监控
            self.monitor = FolderMonitor(folder_path, interval, self.handle_changes, target_window)
            self.monitor_thread = threading.Thread(target=self.monitor.start, daemon=True)
            self.monitor_thread.start()
            
            self.log(f"开始监控文件夹: {folder_path}")
            self.log(f"检测间隔: {interval}秒")
            if target_window:
                self.log(f"检测到变化将自动发送到: {target_window}")
            
            self.stop_button.config(state=tk.NORMAL)
        
        # 启动线程执行监控
        threading.Thread(target=start_monitoring_thread, daemon=True).start()
    
    def load_settings(self):
        """加载保存的设置"""
        self.interval_var.set(self.config.interval)
        self.folder_var.set(self.config.folder_path)
        self.target_var.set(self.config.target_window)
    
    def save_settings(self):
        """保存当前设置"""
        self.config.interval = self.interval_var.get()
        self.config.folder_path = self.folder_var.get()
        self.config.target_window = self.target_var.get()
        self.config.save_config()
        self.log("当前设置已保存")
    
    def stop_monitoring(self):
        """停止监控"""
        if self.monitor:
            self.monitor.stop()
            self.monitor = None
        
        # 更新按钮状态
        self.start_button.config(state=tk.NORMAL)
        self.stop_button.config(state=tk.DISABLED)
        
        self.log("监控已停止")
    
    def handle_changes(self, changes, send_result):
        """处理文件夹变化"""
        self.log("检测到文件夹变化:")
        if changes['added']:
            self.log("  新增文件:")
            for file in changes['added']:
                self.log(f"    + {os.path.basename(file)}")
        if changes['deleted']:
            self.log("  删除文件:")
            for file in changes['deleted']:
                self.log(f"    - {os.path.basename(file)}")
        if changes['modified']:
            self.log("  修改文件:")
            for file in changes['modified']:
                self.log(f"    * {os.path.basename(file)}")
        
        if self.target_var.get():
            if send_result:
                self.log("  变化已成功发送到目标窗口")
            else:
                self.log("  发送到目标窗口失败")
    
    def log(self, message):
        """记录日志"""
        timestamp = time.strftime("%Y-%m-%d %H:%M:%S")
        log_message = f"[{timestamp}] {message}\n"
        
        self.log_text.config(state=tk.NORMAL)
        self.log_text.insert(tk.END, log_message)
        self.log_text.see(tk.END)
        self.log_text.config(state=tk.DISABLED)
    
    def clear_log(self):
        """清空日志"""
        self.log_text.config(state=tk.NORMAL)
        self.log_text.delete(1.0, tk.END)
        self.log_text.config(state=tk.DISABLED)
        self.log("日志已清空")

def main():
    root = tk.Tk()
    app = FolderMonitorApp(root)
    
    # 设置窗口大小和位置
    root.geometry("800x600+100+100")
    root.minsize(600, 400)
    
    # 启动主循环
    root.mainloop()

if __name__ == "__main__":
    main()

转载或参考该代码,请注明出处!多谢合作!!!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Teleger

你的支持是我前进的方向

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值