吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 788|回复: 8
上一主题 下一主题
收起左侧

[Python 转载] 让AI帮忙写的的桌面图标、文件整理工具

[复制链接]
跳转到指定楼层
楼主
兮兮曦 发表于 2025-12-23 11:17 回帖奖励
本帖最后由 兮兮曦 于 2025-12-23 14:15 编辑

软件BUG很多、有些功能没有完善,有没有大佬可以继续完善或者重新写个更好更完美的,直接看图片吧

[Python] 纯文本查看 复制代码
import sys
import json
import os
import uuid
import subprocess
from datetime import datetime
from typing import List, Dict, Any, Optional
from dataclasses import dataclass, field, asdict
from enum import Enum
import mimetypes

from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
from PyQt5.QtGui import *


# 围栏数据类
@dataclass
class FenceItem:
    id: str = field(default_factory=lambda: str(uuid.uuid4()))
    name: str = "图标"
    type: str = "file"
    x: int = 0
    y: int = 0
    size: int = 48
    target_path: str = ""
    icon_path: str = ""  # 图标路径
    grid_x: int = 0  # 网格X坐标
    grid_y: int = 0  # 网格Y坐标
    auto_arranged: bool = True  # 是否自动排列
    custom_icon: str = ""  # 自定义图标路径

    def to_dict(self):
        return asdict(self)

    @classmethod
    def from_dict(cls, data):
        return cls(**data)


@dataclass
class FenceConfig:
    grid_spacing_x: int = 80  # 网格横向间距
    grid_spacing_y: int = 90  # 网格纵向间距
    icon_size: int = 48  # 图标大小
    open_action: str = "double_click"  # 打开方式: single_click, double_click
    auto_arrange: bool = True  # 自动排列
    show_icons: bool = True  # 显示图标
    icon_opacity: float = 1.0  # 图标不透明度
    title_opacity: float = 1.0  # 标题栏不透明度
    text_color: str = "#FFFFFF"  # 图标文字颜色

    def to_dict(self):
        return asdict(self)

    @classmethod
    def from_dict(cls, data):
        return cls(**data)


@dataclass
class Fence:
    id: str = field(default_factory=lambda: str(uuid.uuid4()))
    title: str = "新建围栏"
    color: str = "#4CAF50"
    opacity: float = 0.7  # 围栏背景不透明度
    radius: int = 15
    border_width: int = 1
    x: int = 50
    y: int = 50
    width: int = 250
    height: int = 200
    visible: bool = True
    always_on_top: bool = True  # 是否始终置顶
    items: List[FenceItem] = field(default_factory=list)
    config: FenceConfig = field(default_factory=FenceConfig)

    def to_dict(self):
        data = asdict(self)
        data['config'] = self.config.to_dict()
        return data

    @classmethod
    def from_dict(cls, data):
        fence = cls(**{k: v for k, v in data.items() if k != 'config'})
        fence.config = FenceConfig.from_dict(data.get('config', {}))
        fence.items = [FenceItem.from_dict(item) for item in data.get('items', [])]
        return fence


# 自定义图标标签,支持悬停动画和右键菜单
class IconLabel(QLabel):
    def __init__(self, item, parent=None):
        super().__init__(parent)
        self.item = item
        self.parent_window = parent
        self.setMouseTracking(True)
        self.setCursor(Qt.PointingHandCursor)
        self.scale_animation = None
        self.current_scale = 1.0
        self.is_hovered = False
        self.setup_context_menu()

    def setup_context_menu(self):
        """设置右键菜单"""
        self.setContextMenuPolicy(Qt.CustomContextMenu)
        self.customContextMenuRequested.connect(self.show_context_menu)

    def show_context_menu(self, pos):
        """显示右键菜单"""
        menu = QMenu(self)

        # 打开文件(直接打开)
        open_file_action = QAction("打开文件", self)
        open_file_action.triggered.connect(self.open_file)
        menu.addAction(open_file_action)

        # 打开路径(打开文件所在目录)
        open_path_action = QAction("打开路径", self)
        open_path_action.triggered.connect(self.open_path)
        menu.addAction(open_path_action)

        # 重命名
        rename_action = QAction("重命名", self)
        rename_action.triggered.connect(self.rename_item)
        menu.addAction(rename_action)

        # 替换图标
        replace_icon_action = QAction("替换图标", self)
        replace_icon_action.triggered.connect(self.replace_icon)
        menu.addAction(replace_icon_action)

        menu.addSeparator()

        # 删除
        delete_action = QAction("删除", self)
        delete_action.triggered.connect(self.delete_item)
        menu.addAction(delete_action)

        menu.exec_(self.mapToGlobal(pos))

    def open_file(self):
        """直接打开文件"""
        if self.item.target_path and os.path.exists(self.item.target_path):
            try:
                if sys.platform == "win32":
                    os.startfile(self.item.target_path)
                elif sys.platform == "darwin":
                    subprocess.call(["open", self.item.target_path])
                else:
                    subprocess.call(["xdg-open", self.item.target_path])
            except Exception as e:
                QMessageBox.warning(self, "错误", f"打开文件失败: {e}")

    def open_path(self):
        """打开文件所在目录"""
        if self.item.target_path and os.path.exists(self.item.target_path):
            try:
                file_path = os.path.abspath(self.item.target_path)
                dir_path = os.path.dirname(file_path)

                if sys.platform == "win32":
                    # 在Windows中,使用explorer打开目录并选中文件
                    subprocess.run(['explorer', '/select,', file_path], shell=True)
                elif sys.platform == "darwin":
                    # 在Mac中,使用open打开目录并选中文件
                    subprocess.call(["open", "-R", file_path])
                else:
                    # 在Linux中,使用xdg-open打开目录
                    subprocess.call(["xdg-open", dir_path])
            except Exception as e:
                QMessageBox.warning(self, "错误", f"打开路径失败: {e}")

    def rename_item(self):
        """重命名图标"""
        new_name, ok = QInputDialog.getText(
            self, "重命名图标",
            "请输入新名称:",
            QLineEdit.Normal,
            self.item.name
        )

        if ok and new_name:
            self.item.name = new_name
            # 更新显示
            if self.parent_window:
                self.parent_window.update_icons_display()

    def replace_icon(self):
        """替换图标"""
        # 支持的图标格式
        icon_formats = "图标文件 (*.ico *.png *.jpg *.jpeg *.gif *.bmp);;所有文件 (*.*)"

        file_path, _ = QFileDialog.getOpenFileName(
            self, "选择图标文件",
            "", icon_formats
        )

        if file_path:
            self.item.custom_icon = file_path
            # 更新显示
            if self.parent_window:
                self.parent_window.update_icons_display()

    def delete_item(self):
        """删除图标"""
        reply = QMessageBox.question(
            self, "确认删除",
            f"确定要删除图标 '{self.item.name}' 吗?",
            QMessageBox.Yes | QMessageBox.No,
            QMessageBox.No
        )

        if reply == QMessageBox.Yes:
            # 从父窗口的围栏中移除
            if self.parent_window and self.parent_window.fence:
                self.parent_window.fence.items = [
                    item for item in self.parent_window.fence.items
                    if item.id != self.item.id
                ]
                # 更新显示
                self.parent_window.update_icons_display()

    def enterEvent(self, event):
        """鼠标进入事件"""
        self.is_hovered = True
        if not self.scale_animation:
            self.scale_animation = QVariantAnimation()
            self.scale_animation.setDuration(200)
            self.scale_animation.setEasingCurve(QEasingCurve.OutBack)
            self.scale_animation.valueChanged.connect(self.update_scale)

        self.scale_animation.stop()
        self.scale_animation.setStartValue(1.0)
        self.scale_animation.setEndValue(1.2)
        self.scale_animation.start()
        super().enterEvent(event)

    def leaveEvent(self, event):
        """鼠标离开事件"""
        self.is_hovered = False
        if self.scale_animation:
            self.scale_animation.stop()
            self.scale_animation.setStartValue(self.current_scale)
            self.scale_animation.setEndValue(1.0)
            self.scale_animation.start()
        super().leaveEvent(event)

    def update_scale(self, scale):
        """更新缩放比例"""
        self.current_scale = scale
        # 缩放效果通过paintEvent实现
        self.update()

    def paintEvent(self, event):
        """绘制图标"""
        painter = QPainter(self)
        painter.setRenderHint(QPainter.Antialiasing)

        # 计算缩放后的尺寸
        scaled_size = int(self.parent_window.fence.config.icon_size * self.current_scale)

        # 绘制图标背景
        bg_color = QColor(255, 255, 255, int(50 * self.parent_window.fence.config.icon_opacity))
        painter.setBrush(QBrush(bg_color))
        painter.setPen(Qt.NoPen)
        painter.drawRoundedRect(
            (self.width() - scaled_size) // 2,
            5,  # 顶部留出空间,图标偏上
            scaled_size, scaled_size, 5, 5
        )

        # 绘制图标
        icon_size = int((self.parent_window.fence.config.icon_size - 10) * self.current_scale)

        if self.item.custom_icon and os.path.exists(self.item.custom_icon):
            # 使用自定义图标
            pixmap = QPixmap(self.item.custom_icon)
        elif self.item.target_path and os.path.exists(self.item.target_path):
            # 使用文件图标
            file_info = QFileInfo(self.item.target_path)
            icon = QFileIconProvider().icon(file_info)
            pixmap = icon.pixmap(icon_size, icon_size)
        else:
            # 使用默认图标
            pixmap = QPixmap(icon_size, icon_size)
            pixmap.fill(Qt.transparent)
            painter2 = QPainter(pixmap)
            painter2.setBrush(QColor(100, 150, 255))
            painter2.setPen(Qt.NoPen)
            painter2.drawRoundedRect(2, 2, icon_size - 4, icon_size - 4, 3, 3)
            painter2.end()

        if not pixmap.isNull():
            # 绘制图标(偏上)
            painter.drawPixmap(
                (self.width() - icon_size) // 2,
                5 + (scaled_size - icon_size) // 2,
                icon_size, icon_size,
                pixmap
            )

        # 绘制图标文字(在图标下方)
        # 使用配置中的文字颜色
        text_color = QColor(self.parent_window.fence.config.text_color)
        text_color.setAlpha(int(255 * self.parent_window.fence.config.icon_opacity))

        painter.setPen(QPen(text_color))
        painter.setFont(QFont("Arial", 9))

        # 截断过长的文件名
        display_name = self.item.name[:15]
        if len(self.item.name) > 15:
            display_name = self.item.name[:12] + "..."

        # 文字在图标下方,留出适当间距
        text_y = 5 + scaled_size + 8  # 图标底部 + 8px间距
        text_rect = QRect(0, text_y, self.width(), 20)
        painter.drawText(text_rect, Qt.AlignCenter, display_name)

    def mousePressEvent(self, event):
        """鼠标点击事件"""
        if self.parent_window.fence.config.open_action == "single_click" and event.button() == Qt.LeftButton:
            self.open_file()
        super().mousePressEvent(event)

    def mouseDoubleClickEvent(self, event):
        """鼠标双击事件"""
        if self.parent_window.fence.config.open_action == "double_click" and event.button() == Qt.LeftButton:
            self.open_file()
        super().mouseDoubleClickEvent(event)


# 系统托盘类
class SystemTrayIcon(QSystemTrayIcon):
    def __init__(self, main_window, parent=None):
        super().__init__(parent)
        self.main_window = main_window
        self.setup_tray()

    def setup_tray(self):
        # 创建一个简单的托盘图标
        self.create_tray_icon()

        # 创建托盘菜单
        self.menu = QMenu()

        show_action = QAction("显示主窗口", self)
        show_action.triggered.connect(self.main_window.show_normal)
        self.menu.addAction(show_action)

        hide_action = QAction("隐藏主窗口", self)
        hide_action.triggered.connect(self.main_window.hide)
        self.menu.addAction(hide_action)

        self.menu.addSeparator()

        # 围栏管理子菜单
        fences_menu = QMenu("围栏管理")

        show_all_action = QAction("显示所有围栏", self)
        show_all_action.triggered.connect(self.main_window.show_all_fences)
        fences_menu.addAction(show_all_action)

        hide_all_action = QAction("隐藏所有围栏", self)
        hide_all_action.triggered.connect(self.main_window.hide_all_fences)
        fences_menu.addAction(hide_all_action)

        fences_menu.addSeparator()

        add_fence_action = QAction("添加新围栏", self)
        add_fence_action.triggered.connect(self.main_window.add_fence)
        fences_menu.addAction(add_fence_action)

        self.menu.addMenu(fences_menu)

        self.menu.addSeparator()

        exit_action = QAction("退出程序", self)
        exit_action.triggered.connect(self.main_window.quit_application)
        self.menu.addAction(exit_action)

        self.setContextMenu(self.menu)

        # 双击托盘图标显示/隐藏主窗口
        self.activated.connect(self.on_tray_activated)

    def create_tray_icon(self):
        """创建托盘图标"""
        # 创建一个简单的图标
        pixmap = QPixmap(32, 32)
        pixmap.fill(Qt.transparent)

        painter = QPainter(pixmap)
        painter.setRenderHint(QPainter.Antialiasing)

        # 绘制一个简单的围栏图标
        painter.setBrush(QBrush(QColor("#4CAF50")))
        painter.setPen(QPen(QColor("#2E7D32"), 2))
        painter.drawRoundedRect(4, 4, 24, 24, 5, 5)

        painter.end()

        self.setIcon(QIcon(pixmap))

    def on_tray_activated(self, reason):
        if reason == QSystemTrayIcon.DoubleClick:
            if self.main_window.isVisible():
                self.main_window.hide()
            else:
                self.main_window.show_normal()


# 真实桌面围栏窗口
class RealFenceWindow(QWidget):
    def __init__(self, fence, parent=None):
        super().__init__(parent)
        self.fence = fence
        self.dragging = False
        self.resizing = False
        self.drag_offset = QPoint()
        self.resize_start_pos = QPoint()
        self.resize_start_geo = QRect()
        self.edit_mode = False
        self.icon_labels = []  # 存储图标标签
        self.parent_window = parent  # 保存父窗口引用
        self.status_bar = parent.status_bar if parent else None  # 状态栏引用

        # 创建滚动区域
        self.scroll_area = QScrollArea(self)
        self.scroll_area.setWidgetResizable(True)
        self.scroll_area.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
        self.scroll_area.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded)

        # 设置滚动条样式,使其更宽更明显,同时设置背景透明
        self.scroll_area.setStyleSheet("""
            QScrollArea {
                border: none;
                background: transparent;
            }
            QScrollBar:vertical {
                border: none;
                background: rgba(200, 200, 200, 50);
                width: 12px;
                margin: 0px;
            }
            QScrollBar::handle:vertical {
                background: rgba(100, 100, 100, 150);
                min-height: 20px;
                border-radius: 6px;
            }
            QScrollBar::add-line:vertical, QScrollBar::sub-line:vertical {
                border: none;
                background: none;
            }
            QScrollBar::add-page:vertical, QScrollBar::sub-page:vertical {
                background: none;
            }
        """)

        # 创建图标容器
        self.icon_container = QWidget()
        self.icon_container.setObjectName("iconContainer")
        # 设置图标容器背景为透明
        self.icon_container.setStyleSheet("QWidget#iconContainer { background: transparent; }")
        self.scroll_area.setWidget(self.icon_container)

        # 调整大小手柄区域 - 移动到左下角
        self.resize_handle_size = 12
        self.resize_handle_rect = None
        self.cursor_changed = False

        self.setup_window()
        self.setup_drag_drop()
        self.update_icons_display()

    def setup_window(self):
        """设置窗口属性"""
        # 设置窗口标志
        flags = Qt.FramelessWindowHint | Qt.Tool

        if self.fence.always_on_top:
            flags |= Qt.WindowStaysOnTopHint

        self.setWindowFlags(flags)

        # 设置窗口位置和大小
        self.setGeometry(
            self.fence.x,
            self.fence.y,
            self.fence.width,
            self.fence.height
        )

        # 设置窗口透明
        self.setAttribute(Qt.WA_TranslucentBackground)

        # 设置鼠标跟踪
        self.setMouseTracking(True)

        # 设置右键菜单策略
        self.setContextMenuPolicy(Qt.CustomContextMenu)
        self.customContextMenuRequested.connect(self.show_title_context_menu)

    def show_title_context_menu(self, pos):
        """显示标题栏右键菜单"""
        # 检查是否点击了标题栏区域
        if pos.y() <= 30:
            menu = QMenu(self)

            # 置顶/取消置顶菜单项
            if self.fence.always_on_top:
                toggle_top_action = QAction("取消置顶", self)
            else:
                toggle_top_action = QAction("置顶", self)

            toggle_top_action.triggered.connect(self.toggle_always_on_top)
            menu.addAction(toggle_top_action)

            menu.exec_(self.mapToGlobal(pos))

    def toggle_always_on_top(self, update_flag=True):
        """切换置顶状态"""
        if update_flag:
            self.fence.always_on_top = not self.fence.always_on_top

        # 保存当前几何信息
        current_geometry = self.geometry()

        if self.fence.always_on_top:
            # 设置窗口置顶
            self.setWindowFlags(
                Qt.FramelessWindowHint |
                Qt.WindowStaysOnTopHint |
                Qt.Tool
            )
        else:
            # 取消窗口置顶
            self.setWindowFlags(
                Qt.FramelessWindowHint |
                Qt.Tool
            )

        # 重新显示窗口以应用新的窗口标志
        self.setGeometry(current_geometry)
        self.show()
        self.raise_()

        # 更新状态栏消息
        if self.fence.always_on_top:
            status_msg = "围栏已置顶"
        else:
            status_msg = "围栏取消置顶"

        if self.status_bar:
            self.status_bar.showMessage(status_msg)

    def wheelEvent(self, event):
        """鼠标滚轮事件 - 实现滚动翻页"""
        # 检查是否有滚轮事件且滚动条可见
        if (event.angleDelta().y() != 0 and
                self.scroll_area.verticalScrollBar().isVisible()):

            # 获取滚动的角度
            scroll_delta = event.angleDelta().y()

            # 计算要滚动的步数(每120单位滚动一行)
            steps = scroll_delta // 120

            # 计算每步滚动的像素数(使用网格纵向间距)
            scroll_step = self.fence.config.grid_spacing_y // 2

            # 获取滚动条
            scroll_bar = self.scroll_area.verticalScrollBar()

            # 计算新的滚动位置
            new_value = scroll_bar.value() - (steps * scroll_step)

            # 确保在有效范围内
            new_value = max(scroll_bar.minimum(),
                            min(new_value, scroll_bar.maximum()))

            # 设置滚动位置
            scroll_bar.setValue(new_value)

            # 标记事件已处理
            event.accept()
        else:
            # 如果没有处理,调用父类方法
            super().wheelEvent(event)

    def setup_drag_drop(self):
        """设置拖放功能"""
        self.setAcceptDrops(True)
        self.icon_container.setAcceptDrops(True)

    def paintEvent(self, event):
        """绘制事件"""
        painter = QPainter(self)
        painter.setRenderHint(QPainter.Antialiasing)

        # 绘制围栏背景(使用围栏不透明度)
        bg_color = QColor(self.fence.color)
        bg_color.setAlphaF(self.fence.opacity)

        # 绘制标题栏背景(使用标题栏不透明度)
        title_height = 30
        title_color = QColor(self.fence.color).darker(130)
        title_color.setAlphaF(self.fence.config.title_opacity)

        # 绘制标题栏(上边圆角)
        painter.setPen(QPen(title_color, 0))
        painter.setBrush(QBrush(title_color))
        painter.drawRoundedRect(0, 0, self.width(), title_height,
                                self.fence.radius, self.fence.radius)
        painter.drawRect(0, title_height - self.fence.radius,
                         self.width(), self.fence.radius)

        # 绘制围栏背景(下边圆角)
        painter.setPen(QPen(bg_color, self.fence.border_width))
        painter.setBrush(QBrush(bg_color))

        # 创建内容区域路径
        path = QPainterPath()
        path.moveTo(0, title_height)
        path.lineTo(0, self.height() - self.fence.radius)
        path.quadTo(0, self.height(), self.fence.radius, self.height())
        path.lineTo(self.width() - self.fence.radius, self.height())
        path.quadTo(self.width(), self.height(), self.width(), self.height() - self.fence.radius)
        path.lineTo(self.width(), title_height)
        path.closeSubpath()

        painter.drawPath(path)

        # 绘制标题文字(使用标题栏不透明度)
        title_alpha = int(255 * self.fence.config.title_opacity)
        painter.setPen(QPen(QColor(255, 255, 255, title_alpha)))
        painter.setFont(QFont("Arial", 10, QFont.Bold))
        painter.drawText(QRect(10, 0, self.width() - 20, title_height),
                         Qt.AlignLeft | Qt.AlignVCenter, self.fence.title)

        # 绘制调整大小手柄(始终显示,不依赖编辑模式,放在左下角)
        self.draw_resize_handle(painter)

    def draw_resize_handle(self, painter):
        """绘制调整大小手柄(始终显示,放在左下角)"""
        handle_size = self.resize_handle_size
        handle_x = 0  # 左边
        handle_y = self.height() - handle_size  # 底部

        # 保存手柄矩形区域
        self.resize_handle_rect = QRect(handle_x, handle_y, handle_size, handle_size)

        # 绘制手柄背景
        painter.setBrush(QBrush(QColor(42, 130, 218, 180)))
        painter.setPen(QPen(QColor(42, 130, 218, 220), 1))
        painter.drawRect(self.resize_handle_rect)

        # 绘制手柄图案(对角线)
        painter.setPen(QPen(Qt.white, 1.5))
        padding = 3
        # 左下到右上的对角线
        painter.drawLine(handle_x + padding, handle_y + handle_size - padding,
                         handle_x + handle_size - padding, handle_y + padding)
        # 中间的对角线
        painter.drawLine(handle_x + padding + 2, handle_y + handle_size - padding,
                         handle_x + handle_size - padding, handle_y + padding + 2)
        # 另一条对角线
        painter.drawLine(handle_x + padding, handle_y + handle_size - padding - 2,
                         handle_x + handle_size - padding - 2, handle_y + padding)

    def resizeEvent(self, event):
        """窗口大小改变事件"""
        super().resizeEvent(event)
        # 更新滚动区域大小
        title_height = 30
        self.scroll_area.setGeometry(0, title_height, self.width(), self.height() - title_height)
        self.update_icons_display()

    def update_icons_display(self):
        """更新图标显示"""
        # 清除现有图标标签
        for label in self.icon_labels:
            if label.scale_animation:
                label.scale_animation.stop()
            label.deleteLater()
        self.icon_labels.clear()

        if not self.fence.config.show_icons or not self.fence.items:
            self.icon_container.setFixedSize(self.width() - 20, 0)
            return

        # 计算网格布局
        grid_width = self.fence.config.grid_spacing_x
        grid_height = self.fence.config.grid_spacing_y

        # 计算网格列数
        content_width = self.width() - 20
        cols = max(1, content_width // grid_width)

        # 重新计算所有图标的位置
        for i, item in enumerate(self.fence.items):
            if item.auto_arranged and self.fence.config.auto_arrange:
                # 自动排列
                grid_x = i % cols
                grid_y = i // cols
                item.grid_x = grid_x
                item.grid_y = grid_y
                # 向下移动图标位置,留出标题栏空间
                item.x = 10 + grid_x * grid_width + grid_width // 2
                item.y = 80 + grid_y * grid_height + grid_height // 2
            else:
                # 手动排列,确保位置在边界内
                item.x = max(10 + self.fence.config.icon_size // 2,
                             min(item.x, self.width() - 10 - self.fence.config.icon_size // 2))
                # 对于手动排列,y坐标可以超过窗口高度,通过滚动查看
                # 最小y值为标题栏下方
                min_y = 80 + self.fence.config.icon_size // 2
                item.y = max(min_y, item.y)

        # 创建图标标签
        max_y = 0
        for item in self.fence.items:
            # 创建图标标签
            icon_label = IconLabel(item, self)
            icon_label.setFixedSize(grid_width, grid_height)

            # 计算相对于图标容器的位置
            label_x = item.x - grid_width // 2
            label_y = item.y - grid_height // 2 - 30  # 减去标题栏高度

            icon_label.move(label_x, label_y)
            self.icon_labels.append(icon_label)
            icon_label.show()

            # 记录最大y值以确定容器高度
            max_y = max(max_y, label_y + grid_height)

        # 设置图标容器的大小
        container_width = max(self.width() - 20, cols * grid_width)
        container_height = max(max_y + 20, len(self.fence.items) // max(1, cols) * grid_height + 50)
        self.icon_container.setFixedSize(container_width, container_height)

    def arrange_icon_in_grid(self, item):
        """将图标排列到网格中"""
        grid_width = self.fence.config.grid_spacing_x
        grid_height = self.fence.config.grid_spacing_y

        # 计算网格列数
        content_width = self.width() - 20
        cols = max(1, content_width // grid_width)

        # 找到第一个空闲位置
        for i in range(len(self.fence.items) + 1):
            grid_x = i % cols
            grid_y = i // cols

            # 检查该位置是否被占用
            occupied = False
            for existing_item in self.fence.items:
                if (existing_item.auto_arranged and
                        existing_item.grid_x == grid_x and
                        existing_item.grid_y == grid_y):
                    occupied = True
                    break

            if not occupied:
                item.grid_x = grid_x
                item.grid_y = grid_y
                item.x = 10 + grid_x * grid_width + grid_width // 2
                item.y = 80 + grid_y * grid_height + grid_height // 2
                item.auto_arranged = True
                break

    def rearrange_all_icons(self):
        """重新排列所有图标"""
        if not self.fence.config.auto_arrange:
            return

        # 重置所有图标的网格位置
        for item in self.fence.items:
            if item.auto_arranged:
                item.grid_x = 0
                item.grid_y = 0

        # 重新排列所有图标
        grid_width = self.fence.config.grid_spacing_x
        grid_height = self.fence.config.grid_spacing_y

        # 计算网格列数
        content_width = self.width() - 20
        cols = max(1, content_width // grid_width)

        for i, item in enumerate(self.fence.items):
            if item.auto_arranged:
                grid_x = i % cols
                grid_y = i // cols
                item.grid_x = grid_x
                item.grid_y = grid_y
                item.x = 10 + grid_x * grid_width + grid_width // 2
                item.y = 80 + grid_y * grid_height + grid_height // 2

        self.update_icons_display()

    def mousePressEvent(self, event):
        """鼠标按下事件"""
        if event.button() == Qt.LeftButton:
            pos = event.pos()

            # 检查是否点击了调整大小手柄(始终可用,不依赖编辑模式)
            if self.resize_handle_rect and self.resize_handle_rect.contains(pos):
                self.resizing = True
                self.resize_start_pos = event.globalPos()
                self.resize_start_geo = self.geometry()
                self.setCursor(Qt.SizeBDiagCursor)  # 使用左下角调整光标
                return

            # 开始拖动(点击标题栏)
            if pos.y() <= 30:
                self.dragging = True
                self.drag_offset = event.pos()
                self.setCursor(Qt.ClosedHandCursor)

    def mouseMoveEvent(self, event):
        """鼠标移动事件"""
        pos = event.pos()

        # 更新光标形状
        if not self.dragging and not self.resizing:
            if self.resize_handle_rect and self.resize_handle_rect.contains(pos):
                # 在调整手柄上
                self.setCursor(Qt.SizeBDiagCursor)  # 左下角调整光标
                self.cursor_changed = True
            elif pos.y() <= 30:
                # 在标题栏上
                if not self.cursor_changed:
                    self.setCursor(Qt.OpenHandCursor)
                    self.cursor_changed = True
            else:
                # 在其他区域
                if self.cursor_changed:
                    self.setCursor(Qt.ArrowCursor)
                    self.cursor_changed = False

        # 处理拖动
        if self.dragging:
            # 拖动窗口
            new_pos = self.mapToGlobal(pos - self.drag_offset)
            self.move(new_pos)
            self.fence.x = new_pos.x()
            self.fence.y = new_pos.y()

        # 处理调整大小(始终可用,不依赖编辑模式)
        elif self.resizing:
            # 调整窗口大小(从左下角调整)
            delta = event.globalPos() - self.resize_start_pos
            new_width = max(150, self.resize_start_geo.width() + delta.x())
            new_height = max(120, self.resize_start_geo.height() - delta.y())

            # 计算新的Y位置(保持窗口底部位置不变)
            new_y = self.resize_start_geo.y() + delta.y()

            self.setGeometry(self.resize_start_geo.x(),
                             new_y,
                             new_width, new_height)
            self.fence.x = self.resize_start_geo.x()
            self.fence.y = new_y
            self.fence.width = new_width
            self.fence.height = new_height

            # 调整大小时重新排列图标
            self.rearrange_all_icons()

            self.update()

    def mouseReleaseEvent(self, event):
        """鼠标释放事件"""
        self.dragging = False
        self.resizing = False
        self.setCursor(Qt.ArrowCursor)
        self.cursor_changed = False

    def dragEnterEvent(self, event):
        """拖拽进入事件"""
        if event.mimeData().hasUrls():
            event.acceptProposedAction()

    def dropEvent(self, event):
        """拖放事件"""
        if event.mimeData().hasUrls():
            urls = event.mimeData().urls()
            for url in urls:
                file_path = url.toLocalFile()
                if os.path.exists(file_path):
                    # 获取文件信息
                    file_name = os.path.basename(file_path)

                    # 创建新图标
                    item = FenceItem(
                        name=file_name,
                        type="file",
                        target_path=file_path,
                        size=self.fence.config.icon_size
                    )

                    # 设置位置(如果自动排列)
                    if self.fence.config.auto_arrange:
                        self.arrange_icon_in_grid(item)
                    else:
                        # 使用拖放位置
                        pos = event.pos()
                        item.x = max(10 + self.fence.config.icon_size // 2,
                                     min(pos.x(), self.width() - 10 - self.fence.config.icon_size // 2))
                        # 对于手动排列,y坐标可以超过窗口高度
                        # 最小y值为标题栏下方
                        min_y = 80 + self.fence.config.icon_size // 2
                        item.y = max(min_y, pos.y())

                    self.fence.items.append(item)
                    self.update_icons_display()

        event.acceptProposedAction()

    def update_fence(self, fence):
        """更新围栏属性"""
        self.fence = fence
        self.setGeometry(fence.x, fence.y, fence.width, fence.height)
        self.update_icons_display()
        self.update()

    def closeEvent(self, event):
        """关闭事件"""
        # 停止所有动画
        for label in self.icon_labels:
            if label.scale_animation:
                label.scale_animation.stop()
        super().closeEvent(event)


# 主应用程序
class DesktopFenceApp(QMainWindow):
    def __init__(self):
        super().__init__()
        self.fences = []
        self.real_fence_windows = {}
        self.current_fence_id = None
        self.edit_mode = False

        # 系统托盘
        self.tray_icon = None
        self.setup_tray()

        # 默认设置
        self.default_settings = {
            'opacity': 0.7,
            'radius': 15,
            'border_width': 2,
            'color': QColor("#4CAF50"),
            'show_real_fences': True
        }

        self.setup_ui()
        self.load_fences()

        # 确保围栏窗口在所有窗口之上
        self.raise_fence_windows()

    def setup_ui(self):
        """设置UI"""
        self.setWindowTitle("桌面围栏管理工具")
        self.setGeometry(100, 100, 1000, 700)

        # 创建中央窗口部件
        central_widget = QWidget()
        self.setCentralWidget(central_widget)

        # 主布局
        main_layout = QHBoxLayout(central_widget)

        # 左侧控制面板
        control_panel = QGroupBox("控制面板")
        control_panel.setFixedWidth(350)
        control_layout = QVBoxLayout()

        # 添加围栏按钮
        add_fence_btn = QPushButton("添加新围栏")
        add_fence_btn.clicked.connect(self.add_fence)
        control_layout.addWidget(add_fence_btn)

        # 编辑模式切换
        self.edit_mode_check = QCheckBox("编辑模式")
        self.edit_mode_check.stateChanged.connect(self.toggle_edit_mode)
        control_layout.addWidget(self.edit_mode_check)

        # 显示真实围栏切换
        self.show_fences_check = QCheckBox("显示桌面围栏")
        self.show_fences_check.setChecked(True)
        self.show_fences_check.stateChanged.connect(self.toggle_real_fences)
        control_layout.addWidget(self.show_fences_check)

        # 围栏列表
        control_layout.addWidget(QLabel("围栏列表:"))
        self.fence_list = QListWidget()
        self.fence_list.itemClicked.connect(self.on_fence_select)
        control_layout.addWidget(self.fence_list)

        # 围栏操作按钮
        btn_frame = QWidget()
        btn_layout = QHBoxLayout(btn_frame)

        delete_btn = QPushButton("删除")
        delete_btn.clicked.connect(self.delete_fence)
        btn_layout.addWidget(delete_btn)

        rename_btn = QPushButton("重命名")
        rename_btn.clicked.connect(self.rename_fence)
        btn_layout.addWidget(rename_btn)

        duplicate_btn = QPushButton("复制")
        duplicate_btn.clicked.connect(self.duplicate_fence)
        btn_layout.addWidget(duplicate_btn)

        control_layout.addWidget(btn_frame)

        # 围栏属性设置
        attr_group = QGroupBox("围栏属性")
        attr_layout = QFormLayout()

        # 颜色选择
        self.color_btn = QPushButton()
        self.color_btn.setFixedSize(50, 25)
        self.color_btn.setStyleSheet(f"background-color: {self.default_settings['color'].name()}")
        self.color_btn.clicked.connect(self.choose_color)
        attr_layout.addRow("颜色:", self.color_btn)

        # 围栏背景透明度
        self.opacity_slider = QSlider(Qt.Horizontal)
        self.opacity_slider.setRange(10, 90)
        self.opacity_slider.setValue(int(self.default_settings['opacity'] * 100))
        self.opacity_slider.valueChanged.connect(self.update_opacity)
        attr_layout.addRow("围栏背景透明度:", self.opacity_slider)

        # 标题栏透明度
        self.title_opacity_slider = QSlider(Qt.Horizontal)
        self.title_opacity_slider.setRange(10, 100)
        self.title_opacity_slider.setValue(100)
        self.title_opacity_slider.valueChanged.connect(self.update_title_opacity)
        attr_layout.addRow("标题栏透明度:", self.title_opacity_slider)

        # 图标透明度
        self.icon_opacity_slider = QSlider(Qt.Horizontal)
        self.icon_opacity_slider.setRange(10, 100)
        self.icon_opacity_slider.setValue(100)
        self.icon_opacity_slider.valueChanged.connect(self.update_icon_opacity)
        attr_layout.addRow("图标透明度:", self.icon_opacity_slider)

        # 图标文字颜色
        self.text_color_btn = QPushButton()
        self.text_color_btn.setFixedSize(50, 25)
        self.text_color_btn.setStyleSheet("background-color: #FFFFFF")
        self.text_color_btn.clicked.connect(self.choose_text_color)
        attr_layout.addRow("图标文字颜色:", self.text_color_btn)

        # 圆角半径
        self.radius_slider = QSlider(Qt.Horizontal)
        self.radius_slider.setRange(0, 30)
        self.radius_slider.setValue(self.default_settings['radius'])
        self.radius_slider.valueChanged.connect(self.update_radius)
        attr_layout.addRow("圆角半径:", self.radius_slider)

        # 标题设置
        self.title_edit = QLineEdit("新建围栏")
        self.title_edit.textChanged.connect(self.update_title_realtime)
        attr_layout.addRow("标题:", self.title_edit)

        # 窗口置顶选项
        self.always_on_top_check = QCheckBox("窗口置顶")
        self.always_on_top_check.setChecked(True)
        self.always_on_top_check.stateChanged.connect(self.update_always_on_top)
        attr_layout.addRow("置顶设置:", self.always_on_top_check)

        # 图标设置分组
        icon_group = QGroupBox("图标设置")
        icon_layout = QFormLayout()

        # 图标大小
        self.icon_size_slider = QSlider(Qt.Horizontal)
        self.icon_size_slider.setRange(32, 96)
        self.icon_size_slider.setValue(48)
        self.icon_size_slider.valueChanged.connect(self.update_icon_size)
        icon_layout.addRow("图标大小:", self.icon_size_slider)

        # 网格横向间距
        self.grid_spacing_x_slider = QSlider(Qt.Horizontal)
        self.grid_spacing_x_slider.setRange(60, 120)
        self.grid_spacing_x_slider.setValue(80)
        self.grid_spacing_x_slider.valueChanged.connect(self.update_grid_spacing_x)
        icon_layout.addRow("网格横向间距:", self.grid_spacing_x_slider)

        # 网格纵向间距
        self.grid_spacing_y_slider = QSlider(Qt.Horizontal)
        self.grid_spacing_y_slider.setRange(70, 130)
        self.grid_spacing_y_slider.setValue(90)
        self.grid_spacing_y_slider.valueChanged.connect(self.update_grid_spacing_y)
        icon_layout.addRow("网格纵向间距:", self.grid_spacing_y_slider)

        # 打开方式
        self.open_action_combo = QComboBox()
        self.open_action_combo.addItems(["双击打开", "单击打开"])
        self.open_action_combo.currentIndexChanged.connect(self.update_open_action)
        icon_layout.addRow("打开方式:", self.open_action_combo)

        # 显示图标
        self.show_icons_check = QCheckBox("显示图标")
        self.show_icons_check.setChecked(True)
        self.show_icons_check.stateChanged.connect(self.update_show_icons)
        icon_layout.addRow("", self.show_icons_check)

        # 自动排列
        self.auto_arrange_check = QCheckBox("自动排列")
        self.auto_arrange_check.setChecked(True)
        self.auto_arrange_check.stateChanged.connect(self.update_auto_arrange)
        icon_layout.addRow("", self.auto_arrange_check)

        # 排列图标按钮
        arrange_btn = QPushButton("重新排列图标")
        arrange_btn.clicked.connect(self.arrange_icons)
        icon_layout.addRow("", arrange_btn)

        icon_group.setLayout(icon_layout)
        attr_layout.addRow(icon_group)

        # 应用属性按钮
        apply_btn = QPushButton("应用属性")
        apply_btn.clicked.connect(self.apply_fence_attributes)
        attr_layout.addRow(apply_btn)

        attr_group.setLayout(attr_layout)
        control_layout.addWidget(attr_group)

        # 快速操作
        quick_group = QGroupBox("快速操作")
        quick_layout = QVBoxLayout()

        show_all_btn = QPushButton("全部显示")
        show_all_btn.clicked.connect(self.show_all_fences)
        quick_layout.addWidget(show_all_btn)

        hide_all_btn = QPushButton("全部隐藏")
        hide_all_btn.clicked.connect(self.hide_all_fences)
        quick_layout.addWidget(hide_all_btn)

        raise_all_btn = QPushButton("全部置顶")
        raise_all_btn.clicked.connect(self.raise_all_fences)
        quick_layout.addWidget(raise_all_btn)

        toggle_top_btn = QPushButton("切换置顶状态")
        toggle_top_btn.clicked.connect(self.toggle_all_top_status)
        quick_layout.addWidget(toggle_top_btn)

        quick_group.setLayout(quick_layout)
        control_layout.addWidget(quick_group)

        # 文件操作
        file_group = QGroupBox("文件操作")
        file_layout = QVBoxLayout()

        save_btn = QPushButton("保存配置")
        save_btn.clicked.connect(self.save_fences)
        file_layout.addWidget(save_btn)

        load_btn = QPushButton("加载配置")
        load_btn.clicked.connect(self.load_fences_dialog)
        file_layout.addWidget(load_btn)

        export_btn = QPushButton("导出配置")
        export_btn.clicked.connect(self.export_config)
        file_layout.addWidget(export_btn)

        import_btn = QPushButton("导入配置")
        import_btn.clicked.connect(self.import_config)
        file_layout.addWidget(import_btn)

        file_group.setLayout(file_layout)
        control_layout.addWidget(file_group)

        control_panel.setLayout(control_layout)
        main_layout.addWidget(control_panel)

        # 右侧预览区域
        preview_group = QGroupBox("预览区域")
        preview_layout = QVBoxLayout()

        # 创建画布用于预览
        self.canvas = FenceCanvas(self)
        preview_layout.addWidget(self.canvas)

        preview_group.setLayout(preview_layout)
        main_layout.addWidget(preview_group, 1)

        # 状态栏
        self.status_bar = QStatusBar()
        self.setStatusBar(self.status_bar)
        self.status_bar.showMessage("就绪")

    def choose_text_color(self):
        """选择图标文字颜色"""
        if not self.current_fence_id:
            QMessageBox.warning(self, "警告", "请先选择一个围栏")
            return

        fence = self.get_fence_by_id(self.current_fence_id)
        if not fence:
            return

        color = QColorDialog.getColor(
            QColor(fence.config.text_color),
            self, "选择图标文字颜色"
        )

        if color.isValid():
            self.text_color_btn.setStyleSheet(f"background-color: {color.name()}")
            fence.config.text_color = color.name()

            # 更新真实围栏
            if fence.id in self.real_fence_windows:
                self.real_fence_windows[fence.id].update_icons_display()

            # 更新画布
            self.canvas.update()

    def setup_tray(self):
        """设置系统托盘"""
        if QSystemTrayIcon.isSystemTrayAvailable():
            self.tray_icon = SystemTrayIcon(self)
            self.tray_icon.show()

    def show_normal(self):
        """正常显示窗口"""
        self.showNormal()
        self.activateWindow()
        self.raise_()

    def raise_fence_windows(self):
        """将围栏窗口置顶"""
        for window in self.real_fence_windows.values():
            window.raise_()
            window.activateWindow()

    def add_fence(self):
        """添加新围栏"""
        fence = Fence(
            title=f"新建围栏 {len(self.fences) + 1}",
            opacity=0.7,
            always_on_top=True,
            config=FenceConfig(
                icon_opacity=1.0,
                title_opacity=1.0
            )
        )

        self.fences.append(fence)
        self.update_fence_list()
        self.create_real_fence_window(fence)
        self.select_fence(fence)

        self.status_bar.showMessage(f"已添加围栏: {fence.title}")

    def update_fence_list(self):
        """更新围栏列表"""
        self.fence_list.clear()
        for fence in self.fences:
            # 在围栏名称后添加置顶状态标记
            top_status = " (置顶)" if fence.always_on_top else ""
            item = QListWidgetItem(f"{fence.title}{top_status}")
            item.setData(Qt.UserRole, fence.id)
            self.fence_list.addItem(item)

    def on_fence_select(self, item):
        """选择围栏"""
        fence_id = item.data(Qt.UserRole)
        fence = self.get_fence_by_id(fence_id)
        if fence:
            self.select_fence(fence)

    def select_fence(self, fence):
        """选择围栏"""
        self.current_fence_id = fence.id

        # 更新属性控件
        self.color_btn.setStyleSheet(f"background-color: {fence.color}")
        self.opacity_slider.setValue(int(fence.opacity * 100))
        self.title_opacity_slider.setValue(int(fence.config.title_opacity * 100))
        self.icon_opacity_slider.setValue(int(fence.config.icon_opacity * 100))
        self.text_color_btn.setStyleSheet(f"background-color: {fence.config.text_color}")
        self.radius_slider.setValue(fence.radius)
        self.title_edit.setText(fence.title)
        self.always_on_top_check.setChecked(fence.always_on_top)

        # 更新图标设置
        self.icon_size_slider.setValue(fence.config.icon_size)
        self.grid_spacing_x_slider.setValue(fence.config.grid_spacing_x)
        self.grid_spacing_y_slider.setValue(fence.config.grid_spacing_y)
        self.open_action_combo.setCurrentIndex(
            0 if fence.config.open_action == "double_click" else 1
        )
        self.show_icons_check.setChecked(fence.config.show_icons)
        self.auto_arrange_check.setChecked(fence.config.auto_arrange)

        # 更新画布
        self.canvas.current_fence = fence
        self.canvas.update()

        self.status_bar.showMessage(f"已选择围栏: {fence.title}")

    def get_fence_by_id(self, fence_id):
        """根据ID获取围栏"""
        for fence in self.fences:
            if fence.id == fence_id:
                return fence
        return None

    def create_real_fence_window(self, fence):
        """创建真实桌面围栏窗口"""
        if not self.show_fences_check.isChecked():
            return

        window = RealFenceWindow(fence, self)
        window.edit_mode = self.edit_mode
        window.show()
        window.raise_()

        self.real_fence_windows[fence.id] = window

    def toggle_edit_mode(self, state):
        """切换编辑模式"""
        self.edit_mode = state == Qt.Checked

        # 安全地更新所有真实围栏窗口
        for fence_id, window in list(self.real_fence_windows.items()):
            try:
                window.edit_mode = self.edit_mode
                window.update()  # 触发重绘
            except Exception as e:
                print(f"更新围栏窗口时出错: {e}")
                # 如果窗口有问题,重新创建
                fence = self.get_fence_by_id(fence_id)
                if fence:
                    self.create_real_fence_window(fence)

        self.status_bar.showMessage(f"编辑模式: {'开启' if self.edit_mode else '关闭'}")

    def toggle_real_fences(self, state):
        """切换真实围栏显示"""
        show = state == Qt.Checked

        if show:
            for fence in self.fences:
                if fence.id not in self.real_fence_windows:
                    self.create_real_fence_window(fence)
        else:
            for window in list(self.real_fence_windows.values()):
                window.close()
            self.real_fence_windows.clear()

    def choose_color(self):
        """选择颜色"""
        if not self.current_fence_id:
            QMessageBox.warning(self, "警告", "请先选择一个围栏")
            return

        fence = self.get_fence_by_id(self.current_fence_id)
        if not fence:
            return

        color = QColorDialog.getColor(
            QColor(fence.color),
            self, "选择围栏颜色"
        )

        if color.isValid():
            self.color_btn.setStyleSheet(f"background-color: {color.name()}")
            fence.color = color.name()

            # 更新真实围栏
            if fence.id in self.real_fence_windows:
                self.real_fence_windows[fence.id].update()

            # 更新画布
            self.canvas.update()

    def update_opacity(self, value):
        """更新围栏背景透明度"""
        if self.current_fence_id:
            fence = self.get_fence_by_id(self.current_fence_id)
            if fence:
                fence.opacity = value / 100.0

                # 更新真实围栏
                if fence.id in self.real_fence_windows:
                    self.real_fence_windows[fence.id].update()

                # 更新画布
                self.canvas.update()

    def update_title_opacity(self, value):
        """更新标题栏透明度"""
        if self.current_fence_id:
            fence = self.get_fence_by_id(self.current_fence_id)
            if fence:
                fence.config.title_opacity = value / 100.0

                # 更新真实围栏
                if fence.id in self.real_fence_windows:
                    self.real_fence_windows[fence.id].update()

                # 更新画布
                self.canvas.update()

    def update_icon_opacity(self, value):
        """更新图标透明度"""
        if self.current_fence_id:
            fence = self.get_fence_by_id(self.current_fence_id)
            if fence:
                fence.config.icon_opacity = value / 100.0

                # 更新真实围栏
                if fence.id in self.real_fence_windows:
                    self.real_fence_windows[fence.id].update_icons_display()

                # 更新画布
                self.canvas.update()

    def update_radius(self, value):
        """更新圆角半径"""
        if self.current_fence_id:
            fence = self.get_fence_by_id(self.current_fence_id)
            if fence:
                fence.radius = value

                # 更新真实围栏
                if fence.id in self.real_fence_windows:
                    self.real_fence_windows[fence.id].update()

                # 更新画布
                self.canvas.update()

    def update_title_realtime(self, text):
        """实时更新标题"""
        if self.current_fence_id:
            fence = self.get_fence_by_id(self.current_fence_id)
            if fence and text.strip():
                fence.title = text.strip()

                # 更新列表
                self.update_fence_list()

                # 更新真实围栏
                if fence.id in self.real_fence_windows:
                    self.real_fence_windows[fence.id].update()

                # 更新画布
                self.canvas.update()

    def update_always_on_top(self, state):
        """更新置顶设置"""
        if self.current_fence_id:
            fence = self.get_fence_by_id(self.current_fence_id)
            if fence:
                fence.always_on_top = state == Qt.Checked

                # 更新真实围栏
                if fence.id in self.real_fence_windows:
                    window = self.real_fence_windows[fence.id]
                    window.toggle_always_on_top(False)  # 不更新标志,因为已经在上面更新了

                # 更新围栏列表显示
                self.update_fence_list()

    def update_icon_size(self, value):
        """更新图标大小"""
        if self.current_fence_id:
            fence = self.get_fence_by_id(self.current_fence_id)
            if fence:
                fence.config.icon_size = value

                # 更新所有图标大小
                for item in fence.items:
                    item.size = value

                # 更新真实围栏
                if fence.id in self.real_fence_windows:
                    self.real_fence_windows[fence.id].update_icons_display()

                # 更新画布
                self.canvas.update()

    def update_grid_spacing_x(self, value):
        """更新网格横向间距"""
        if self.current_fence_id:
            fence = self.get_fence_by_id(self.current_fence_id)
            if fence:
                fence.config.grid_spacing_x = value

                # 更新真实围栏
                if fence.id in self.real_fence_windows:
                    self.real_fence_windows[fence.id].rearrange_all_icons()

                # 更新画布
                self.canvas.update()

    def update_grid_spacing_y(self, value):
        """更新网格纵向间距"""
        if self.current_fence_id:
            fence = self.get_fence_by_id(self.current_fence_id)
            if fence:
                fence.config.grid_spacing_y = value

                # 更新真实围栏
                if fence.id in self.real_fence_windows:
                    self.real_fence_windows[fence.id].rearrange_all_icons()

                # 更新画布
                self.canvas.update()

    def update_open_action(self, index):
        """更新打开方式"""
        if self.current_fence_id:
            fence = self.get_fence_by_id(self.current_fence_id)
            if fence:
                fence.config.open_action = "double_click" if index == 0 else "single_click"

    def update_show_icons(self, state):
        """更新显示图标设置"""
        if self.current_fence_id:
            fence = self.get_fence_by_id(self.current_fence_id)
            if fence:
                fence.config.show_icons = state == Qt.Checked

                # 更新真实围栏
                if fence.id in self.real_fence_windows:
                    self.real_fence_windows[fence.id].update_icons_display()

                # 更新画布
                self.canvas.update()

    def update_auto_arrange(self, state):
        """更新自动排列设置"""
        if self.current_fence_id:
            fence = self.get_fence_by_id(self.current_fence_id)
            if fence:
                fence.config.auto_arrange = state == Qt.Checked

    def arrange_icons(self):
        """重新排列图标"""
        if self.current_fence_id:
            fence = self.get_fence_by_id(self.current_fence_id)
            if fence:
                # 更新真实围栏
                if fence.id in self.real_fence_windows:
                    self.real_fence_windows[fence.id].rearrange_all_icons()

                # 更新画布
                self.canvas.update()

    def apply_fence_attributes(self):
        """应用围栏属性"""
        if not self.current_fence_id:
            QMessageBox.warning(self, "警告", "请先选择一个围栏")
            return

        fence = self.get_fence_by_id(self.current_fence_id)
        if not fence:
            return

        # 更新颜色
        color_style = self.color_btn.styleSheet()
        if "background-color:" in color_style:
            color_start = color_style.find("#")
            if color_start != -1:
                fence.color = color_style[color_start:color_start + 7]

        # 更新文字颜色
        text_color_style = self.text_color_btn.styleSheet()
        if "background-color:" in text_color_style:
            color_start = text_color_style.find("#")
            if color_start != -1:
                fence.config.text_color = text_color_style[color_start:color_start + 7]

        # 更新透明度
        fence.opacity = self.opacity_slider.value() / 100.0
        fence.config.title_opacity = self.title_opacity_slider.value() / 100.0
        fence.config.icon_opacity = self.icon_opacity_slider.value() / 100.0

        # 更新圆角半径
        fence.radius = self.radius_slider.value()

        # 更新标题
        fence.title = self.title_edit.text().strip()

        # 更新置顶设置
        fence.always_on_top = self.always_on_top_check.isChecked()

        # 更新图标设置
        fence.config.icon_size = self.icon_size_slider.value()
        fence.config.grid_spacing_x = self.grid_spacing_x_slider.value()
        fence.config.grid_spacing_y = self.grid_spacing_y_slider.value()
        fence.config.open_action = "double_click" if self.open_action_combo.currentIndex() == 0 else "single_click"
        fence.config.show_icons = self.show_icons_check.isChecked()
        fence.config.auto_arrange = self.auto_arrange_check.isChecked()

        # 更新列表
        self.update_fence_list()

        # 更新真实围栏
        if fence.id in self.real_fence_windows:
            window = self.real_fence_windows[fence.id]
            window.update_fence(fence)

        # 更新画布
        self.canvas.update()

        self.status_bar.showMessage(f"已更新围栏属性: {fence.title}")

    def delete_fence(self):
        """删除围栏"""
        if not self.current_fence_id:
            QMessageBox.warning(self, "警告", "请先选择一个围栏")
            return

        fence = self.get_fence_by_id(self.current_fence_id)
        if not fence:
            return

        reply = QMessageBox.question(
            self, "确认",
            f"确定要删除围栏 '{fence.title}' 吗?",
            QMessageBox.Yes | QMessageBox.No,
            QMessageBox.No
        )

        if reply == QMessageBox.Yes:
            # 删除真实围栏窗口
            if fence.id in self.real_fence_windows:
                self.real_fence_windows[fence.id].close()
                del self.real_fence_windows[fence.id]

            # 从列表中删除
            self.fences.remove(fence)
            self.current_fence_id = None

            # 更新UI
            self.update_fence_list()
            self.canvas.current_fence = None
            self.canvas.update()

            self.status_bar.showMessage("围栏已删除")

    def rename_fence(self):
        """重命名围栏"""
        if not self.current_fence_id:
            QMessageBox.warning(self, "警告", "请先选择一个围栏")
            return

        fence = self.get_fence_by_id(self.current_fence_id)
        if not fence:
            return

        new_name, ok = QInputDialog.getText(
            self, "重命名围栏",
            "请输入新名称:",
            QLineEdit.Normal,
            fence.title
        )

        if ok and new_name:
            fence.title = new_name

            # 更新UI
            self.title_edit.setText(new_name)
            self.update_fence_list()

            # 更新真实围栏
            if fence.id in self.real_fence_windows:
                self.real_fence_windows[fence.id].update()

            # 更新画布
            self.canvas.update()

            self.status_bar.showMessage(f"围栏已重命名为: {new_name}")

    def duplicate_fence(self):
        """复制围栏"""
        if not self.current_fence_id:
            QMessageBox.warning(self, "警告", "请先选择一个围栏")
            return

        fence = self.get_fence_by_id(self.current_fence_id)
        if not fence:
            return

        # 创建副本
        new_fence = Fence.from_dict(fence.to_dict())
        new_fence.id = str(uuid.uuid4())
        new_fence.title = f"{fence.title} 副本"
        new_fence.x += 20
        new_fence.y += 20

        # 复制图标
        new_fence.items = [FenceItem.from_dict(item.to_dict()) for item in fence.items]

        self.fences.append(new_fence)
        self.update_fence_list()
        self.create_real_fence_window(new_fence)

        self.status_bar.showMessage(f"已复制围栏: {new_fence.title}")

    def show_all_fences(self):
        """显示所有围栏"""
        self.show_fences_check.setChecked(True)
        for fence in self.fences:
            if fence.id not in self.real_fence_windows:
                self.create_real_fence_window(fence)
        self.status_bar.showMessage("已显示所有围栏")

    def hide_all_fences(self):
        """隐藏所有围栏"""
        self.show_fences_check.setChecked(False)
        for window in list(self.real_fence_windows.values()):
            window.close()
        self.real_fence_windows.clear()
        self.status_bar.showMessage("已隐藏所有围栏")

    def raise_all_fences(self):
        """将所有围栏置顶"""
        for window in self.real_fence_windows.values():
            window.raise_()
        self.status_bar.showMessage("已将所有围栏置顶")

    def toggle_all_top_status(self):
        """切换所有围栏的置顶状态"""
        if not self.fences:
            self.status_bar.showMessage("没有围栏可操作")
            return

        # 确定新状态(如果有置顶的,就全部取消置顶;反之全部置顶)
        any_on_top = any(fence.always_on_top for fence in self.fences)
        new_status = not any_on_top

        for fence in self.fences:
            fence.always_on_top = new_status
            if fence.id in self.real_fence_windows:
                self.real_fence_windows[fence.id].toggle_always_on_top(False)

        # 更新UI
        self.update_fence_list()
        if self.current_fence_id:
            fence = self.get_fence_by_id(self.current_fence_id)
            if fence:
                self.always_on_top_check.setChecked(fence.always_on_top)

        status_text = "全部置顶" if new_status else "全部取消置顶"
        self.status_bar.showMessage(f"已{status_text}")

    def save_fences(self):
        """保存配置"""
        try:
            save_data = {
                'fences': [fence.to_dict() for fence in self.fences],
                'settings': {
                    'show_real_fences': self.show_fences_check.isChecked(),
                    'edit_mode': self.edit_mode
                }
            }

            with open('desktop_fences.json', 'w', encoding='utf-8') as f:
                json.dump(save_data, f, indent=2, ensure_ascii=False)

            self.status_bar.showMessage("配置已保存到 desktop_fences.json")
            QMessageBox.information(self, "成功", "配置保存成功")

        except Exception as e:
            QMessageBox.critical(self, "错误", f"保存失败: {str(e)}")

    def load_fences(self):
        """加载配置"""
        try:
            config_files = ['desktop_fences.json', 'fences_config.json']
            config_file = None

            for file in config_files:
                if os.path.exists(file):
                    config_file = file
                    break

            if config_file:
                with open(config_file, 'r', encoding='utf-8') as f:
                    save_data = json.load(f)

                # 加载围栏
                self.fences.clear()
                for fence_data in save_data.get('fences', []):
                    fence = Fence.from_dict(fence_data)
                    self.fences.append(fence)

                # 加载设置
                settings = save_data.get('settings', {})
                self.show_fences_check.setChecked(settings.get('show_real_fences', True))
                self.edit_mode_check.setChecked(settings.get('edit_mode', False))

                # 更新UI
                self.update_fence_list()

                # 创建真实围栏窗口
                if self.show_fences_check.isChecked():
                    for fence in self.fences:
                        self.create_real_fence_window(fence)

                self.status_bar.showMessage(f"配置已从 {config_file} 加载")

        except Exception as e:
            print(f"加载配置失败: {e}")

    def load_fences_dialog(self):
        """从对话框加载配置"""
        filename, _ = QFileDialog.getOpenFileName(
            self, "选择配置文件",
            "", "JSON文件 (*.json);;所有文件 (*.*)"
        )

        if filename:
            try:
                with open(filename, 'r', encoding='utf-8') as f:
                    save_data = json.load(f)

                # 清除现有围栏
                for window in list(self.real_fence_windows.values()):
                    window.close()
                self.real_fence_windows.clear()
                self.fences.clear()

                # 加载新围栏
                for fence_data in save_data.get('fences', []):
                    fence = Fence.from_dict(fence_data)
                    self.fences.append(fence)

                # 加载设置
                settings = save_data.get('settings', {})
                self.show_fences_check.setChecked(settings.get('show_real_fences', True))
                self.edit_mode_check.setChecked(settings.get('edit_mode', False))

                # 更新UI
                self.update_fence_list()

                # 创建真实围栏窗口
                if self.show_fences_check.isChecked():
                    for fence in self.fences:
                        self.create_real_fence_window(fence)

                self.status_bar.showMessage(f"已加载配置文件: {os.path.basename(filename)}")

            except Exception as e:
                QMessageBox.critical(self, "错误", f"加载失败: {str(e)}")

    def import_config(self):
        """导入配置"""
        self.load_fences_dialog()

    def export_config(self):
        """导出配置"""
        filename, _ = QFileDialog.getSaveFileName(
            self, "导出配置",
            "", "JSON文件 (*.json);;所有文件 (*.*)"
        )

        if filename:
            try:
                save_data = {
                    'fences': [fence.to_dict() for fence in self.fences],
                    'settings': {
                        'show_real_fences': self.show_fences_check.isChecked(),
                        'edit_mode': self.edit_mode
                    }
                }

                with open(filename, 'w', encoding='utf-8') as f:
                    json.dump(save_data, f, indent=2, ensure_ascii=False)

                self.status_bar.showMessage(f"配置已导出到: {os.path.basename(filename)}")
                QMessageBox.information(self, "成功", "配置导出成功")

            except Exception as e:
                QMessageBox.critical(self, "错误", f"导出失败: {str(e)}")

    def quit_application(self):
        """退出应用程序"""
        # 保存配置
        try:
            save_data = {
                'fences': [fence.to_dict() for fence in self.fences],
                'settings': {
                    'show_real_fences': self.show_fences_check.isChecked(),
                    'edit_mode': self.edit_mode
                }
            }

            with open('desktop_fences.json', 'w', encoding='utf-8') as f:
                json.dump(save_data, f, indent=2, ensure_ascii=False)

        except Exception as e:
            print(f"自动保存失败: {e}")

        # 关闭所有围栏窗口
        for window in list(self.real_fence_windows.values()):
            window.close()

        # 退出应用程序
        QApplication.quit()

    def closeEvent(self, event):
        """窗口关闭事件"""
        # 隐藏窗口而不是关闭
        self.hide()
        event.ignore()

        # 显示托盘提示
        if self.tray_icon:
            self.tray_icon.showMessage(
                "桌面围栏",
                "程序已最小化到系统托盘\n右键托盘图标可管理围栏",
                QSystemTrayIcon.Information,
                3000
            )


# 画布类(用于预览)
class FenceCanvas(QWidget):
    def __init__(self, parent=None):
        super().__init__(parent)
        self.current_fence = None
        self.setAcceptDrops(True)
        self.setMouseTracking(True)

    def paintEvent(self, event):
        painter = QPainter(self)
        painter.setRenderHint(QPainter.Antialiasing)

        # 绘制网格背景
        self.draw_grid(painter)

        # 绘制当前选中的围栏
        if self.current_fence:
            self.draw_fence(painter, self.current_fence)

    def draw_grid(self, painter):
        """绘制网格"""
        width = self.width()
        height = self.height()
        grid_size = 20

        painter.setPen(QPen(QColor(255, 255, 255, 25), 1))

        for x in range(0, width, grid_size):
            painter.drawLine(x, 0, x, height)
        for y in range(0, height, grid_size):
            painter.drawLine(0, y, width, y)

    def draw_fence(self, painter, fence):
        """绘制围栏"""
        # 确保围栏大小合理
        fence.width = max(150, fence.width)
        fence.height = max(120, fence.height)

        # 绘制标题栏(上边圆角)
        title_height = 30
        title_color = QColor(fence.color).darker(130)
        title_color.setAlphaF(fence.config.title_opacity)

        painter.setPen(QPen(title_color, 0))
        painter.setBrush(QBrush(title_color))
        painter.drawRoundedRect(fence.x, fence.y, fence.width, title_height,
                                fence.radius, fence.radius)
        painter.drawRect(fence.x, fence.y + title_height - fence.radius,
                         fence.width, fence.radius)

        # 绘制围栏背景(下边圆角)
        bg_color = QColor(fence.color)
        bg_color.setAlphaF(fence.opacity)

        painter.setPen(QPen(bg_color, fence.border_width))
        painter.setBrush(QBrush(bg_color))

        # 创建内容区域路径
        path = QPainterPath()
        path.moveTo(fence.x, fence.y + title_height)
        path.lineTo(fence.x, fence.y + fence.height - fence.radius)
        path.quadTo(fence.x, fence.y + fence.height,
                    fence.x + fence.radius, fence.y + fence.height)
        path.lineTo(fence.x + fence.width - fence.radius, fence.y + fence.height)
        path.quadTo(fence.x + fence.width, fence.y + fence.height,
                    fence.x + fence.width, fence.y + fence.height - fence.radius)
        path.lineTo(fence.x + fence.width, fence.y + title_height)
        path.closeSubpath()

        painter.drawPath(path)

        # 绘制标题文字
        title_alpha = int(255 * fence.config.title_opacity)
        painter.setPen(QPen(QColor(255, 255, 255, title_alpha)))
        painter.setFont(QFont("Arial", 10, QFont.Bold))
        painter.drawText(QRect(fence.x + 10, fence.y, fence.width - 20, title_height),
                         Qt.AlignLeft | Qt.AlignVCenter, fence.title)

        # 绘制图标
        if fence.config.show_icons:
            self.draw_icons(painter, fence)

    def draw_icons(self, painter, fence):
        """绘制图标"""
        if not fence.items:
            return

        # 计算网格布局
        grid_width = fence.config.grid_spacing_x
        grid_height = fence.config.grid_spacing_y

        # 计算网格列数
        content_width = fence.width - 20
        cols = max(1, content_width // grid_width)

        # 按照网格位置排序
        sorted_items = sorted(fence.items, key=lambda item: (item.grid_y, item.grid_x))

        for i, item in enumerate(sorted_items):
            if i >= 50:  # 限制显示数量
                break

            # 计算网格位置
            grid_x = item.grid_x if item.auto_arranged else i % cols
            grid_y = item.grid_y if item.auto_arranged else i // cols

            # 计算实际位置
            icon_x = fence.x + 10 + grid_x * grid_width + grid_width // 2
            icon_y = fence.y + 50 + grid_y * grid_height + grid_height // 2

            # 设置图标不透明度
            icon_opacity = fence.config.icon_opacity

            # 绘制图标背景(圆角矩形)
            bg_color = QColor(255, 255, 255, int(30 * icon_opacity))
            border_color = QColor(255, 255, 255, int(50 * icon_opacity))

            painter.setBrush(QBrush(bg_color))
            painter.setPen(QPen(border_color, 1))

            icon_size = fence.config.icon_size
            # 图标位置偏上,为文字留出空间
            painter.drawRoundedRect(icon_x - icon_size // 2, icon_y - icon_size // 2 - 5,
                                    icon_size, icon_size, 5, 5)

            # 绘制图标文字(在图标下方)- 使用配置中的文字颜色
            text_color = QColor(fence.config.text_color)
            text_color.setAlpha(int(255 * icon_opacity))
            painter.setPen(QPen(text_color))
            painter.setFont(QFont("Arial", 9))

            # 截断过长的文件名
            display_name = item.name[:15]
            if len(item.name) > 15:
                display_name = item.name[:12] + "..."

            # 文字在图标下方
            text_rect = QRect(icon_x - 40, icon_y + icon_size // 2 - 5 + 8, 80, 20)
            painter.drawText(text_rect, Qt.AlignCenter, display_name)

    def dragEnterEvent(self, event):
        """拖拽进入事件"""
        if event.mimeData().hasUrls():
            event.acceptProposedAction()

    def dropEvent(self, event):
        """拖放事件"""
        if event.mimeData().hasUrls():
            urls = event.mimeData().urls()
            for url in urls:
                file_path = url.toLocalFile()
                if os.path.exists(file_path) and self.current_fence:
                    # 创建图标
                    file_name = os.path.basename(file_path)

                    item = FenceItem(
                        name=file_name,
                        type="file",
                        target_path=file_path,
                        size=self.current_fence.config.icon_size
                    )

                    # 自动排列
                    if self.current_fence.config.auto_arrange:
                        # 计算网格位置
                        grid_width = self.current_fence.config.grid_spacing_x
                        grid_height = self.current_fence.config.grid_spacing_y

                        # 计算网格列数
                        content_width = self.current_fence.width - 20
                        cols = max(1, content_width // grid_width)

                        # 找到第一个空闲位置
                        for i in range(len(self.current_fence.items), 100):
                            grid_x = i % cols
                            grid_y = i // cols

                            occupied = False
                            for existing_item in self.current_fence.items:
                                if (existing_item.auto_arranged and
                                        existing_item.grid_x == grid_x and
                                        existing_item.grid_y == grid_y):
                                    occupied = True
                                    break

                            if not occupied:
                                item.grid_x = grid_x
                                item.grid_y = grid_y
                                item.auto_arranged = True
                                break

                    self.current_fence.items.append(item)

                    # 更新真实围栏
                    app = self.window()
                    if self.current_fence.id in app.real_fence_windows:
                        app.real_fence_windows[self.current_fence.id].update_icons_display()

                    self.update()
                    break

        event.acceptProposedAction()


def main():
    app = QApplication(sys.argv)
    app.setApplicationName("桌面围栏管理工具")
    app.setQuitOnLastWindowClosed(False)

    # 设置应用程序样式
    app.setStyle("Fusion")

    # 设置深色主题
    palette = QPalette()
    palette.setColor(QPalette.Window, QColor(53, 53, 53))
    palette.setColor(QPalette.WindowText, Qt.white)
    palette.setColor(QPalette.Base, QColor(25, 25, 25))
    palette.setColor(QPalette.AlternateBase, QColor(53, 53, 53))
    palette.setColor(QPalette.ToolTipBase, Qt.white)
    palette.setColor(QPalette.ToolTipText, Qt.white)
    palette.setColor(QPalette.Text, Qt.white)
    palette.setColor(QPalette.Button, QColor(53, 53, 53))
    palette.setColor(QPalette.ButtonText, Qt.white)
    palette.setColor(QPalette.BrightText, Qt.red)
    palette.setColor(QPalette.Link, QColor(42, 130, 218))
    palette.setColor(QPalette.Highlight, QColor(42, 130, 218))
    palette.setColor(QPalette.HighlightedText, Qt.black)
    app.setPalette(palette)

    # 创建主窗口
    window = DesktopFenceApp()
    window.show()

    sys.exit(app.exec_())


if __name__ == "__main__":
    main()

源码加上打包好软件
https://wudongqingcheng.lanzouq.com/b014wxu1fi
密码:c1rv



v1.8  又让改了一版,等大佬写了


https://wudongqingcheng.lanzouq.com/iXRVW3eafkjc

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

推荐
lujkhua 发表于 2025-12-24 08:10


让AI帮忙修改悦目点儿


QQ20251224-080622.jpg (57.1 KB, 下载次数: 0)

QQ20251224-080622.jpg
沙发
guoerluosi 发表于 2025-12-23 12:52
3#
sylovek 发表于 2025-12-23 12:54
厉害了,我也让ai写过,但感觉差强人意,不咋会改,还需要学习
4#
hurric 发表于 2025-12-23 13:02
这个可以哦 测试一下
5#
sfl4800 发表于 2025-12-23 13:32
控制面板太夸张了。
6#
zzyblacksky 发表于 2025-12-23 23:22
厉害啊,可以让自己的员工干活输出了
7#
NikonZ7II 发表于 2025-12-24 07:06
感谢楼主分享,还没怎么使用过AI类,学习学习
9#
iSummer999 发表于 2025-12-24 11:40
能支持改背景图吗
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

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

GMT+8, 2025-12-25 15:02

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

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