Qt Windows版本的《智能鼠标点击器》

最近使用QT结合AI整了一款Windows端的鼠标点击器,可以帮我我们解放双手,按照既定的xy坐标去处理点击动作!

软件截图:

操作流程

1.录入动作

(已更新2.0)

2.执行序列动作

(已更新2.0)

3.重复执行序列动作

(已更新2.0)

4.鼠标连续点击测试

(已更新2.0)


源码分享

.pro文件

QT       += core gui

greaterThan(QT_MAJOR_VERSION, 4): QT += widgets

CONFIG += c++11

TARGET = WinMouseClicker
TEMPLATE = app

SOURCES += \
    main.cpp \
    mainwindow.cpp

HEADERS += \
    mainwindow.h

# Windows 平台需要链接 user32 库
win32 {
    LIBS += -luser32
}

# ========== 编码设置 ==========
# 对于 MSVC 编译器,添加 UTF-8 支持
win32-msvc* {
    QMAKE_CXXFLAGS += /utf-8
}

# 对于 MinGW 编译器
win32-g++ {
    QMAKE_CXXFLAGS += -finput-charset=UTF-8 -fexec-charset=UTF-8
}

# 设置源代码编码为 UTF-8
CODECFORSRC = UTF-8

mainwindow.h

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include <windows.h>
#include <vector>

QT_BEGIN_NAMESPACE
class QPushButton;
class QDoubleSpinBox;
class QSpinBox;
class QLabel;
class QCheckBox;
class QTimer;
class QTableWidget;
class QTableWidgetItem;
class QProgressBar;
class QMenu;
QT_END_NAMESPACE

// 动作数据结构
struct ClickAction {
    int id;
    int x;
    int y;
    double delay;      // 延迟时间(秒)
    bool leftButton;
    bool doubleClick;
    QString description;

    ClickAction(int id = 0, int x = 0, int y = 0, double delay = 0.0,
                bool leftButton = true, bool doubleClick = false,
                const QString& desc = "")
        : id(id), x(x), y(y), delay(delay),
          leftButton(leftButton), doubleClick(doubleClick),
          description(desc) {}
};

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    MainWindow(QWidget *parent = nullptr);
    ~MainWindow();

protected:
    bool eventFilter(QObject *obj, QEvent *event) override;
    bool nativeEvent(const QByteArray &eventType, void *message, long *result) override;
    void resizeEvent(QResizeEvent *event) override;

private slots:
    void onClickButtonClicked();      // 点击按钮槽函数
    void onPickCoordClicked();        // 拾取坐标槽函数
    void onRepeatTimeout();           // 重复点击定时器

    // 动作序列相关槽函数
    void onAddActionClicked();        // 添加动作
    void onRemoveActionClicked();     // 移除动作
    void onClearActionsClicked();     // 清空动作
    void onExecuteSequenceClicked();  // 执行序列
    void onRepeatSequenceClicked();   // 重复执行序列
    void onStopSequenceClicked();     // 停止序列
    void onSequenceTimeout();         // 序列定时器

    // 倒计时相关
    void onCountdownTimeout();        // 倒计时定时器

    // 表格相关
    void onActionTableItemChanged(QTableWidgetItem *item);
    void onActionTableSelectionChanged();

    // 表格右键菜单
    void onTableContextMenu(const QPoint &pos);
    void onInsertAction();            // 插入动作
    void onMoveActionUp();            // 上移动作
    void onMoveActionDown();          // 下移动作
    void onDuplicateAction();         // 复制动作

    // 复选框状态改变
    void onRepeatStateChanged(int state);
    void onRepeatCountChanged(int value);
    void onInfiniteSequenceRepeatChanged(int state);

    // 导入导出功能
    void onExportActionsClicked();    // 导出动作序列
    void onImportActionsClicked();    // 导入动作序列

private:
    // Windows API 鼠标点击函数
    void clickAt(int x, int y, bool leftButton = true, bool doubleClick = false);
    void clickAction(const ClickAction& action);

    // 移动鼠标到指定位置
    void moveMouseTo(int x, int y);

    // 创建鼠标点击事件
    void sendMouseClick(int x, int y, bool leftButton, bool down);

    // 动作序列管理
    void addActionToList(const ClickAction& action);
    void updateActionTable();
    void createTableContextMenu();    // 创建表格右键菜单

    // 表格验证
    bool validateTableInput(int row, int column, const QString& text);
    void setupTableDelegates();       // 设置表格委托

    // 快捷键设置
    void setupShortcuts();

    // 开始倒计时
    void startCountdown(int seconds);

    // 转换延迟值为毫秒
    int delayToMilliseconds(double seconds);

    // 执行序列相关
    void startSequenceExecution(bool repeatMode = false);

    // 更新进度显示
    void updateProgressInfo();

    // 显示状态消息
    void showStatusMessage(const QString& message, bool isError = false, bool isSuccess = false);

    // 激活窗口并显示在最前面
    void activateAndShowWindow();

    // 注册全局热键
    void registerGlobalHotkey();

    // 注销全局热键
    void unregisterGlobalHotkey();

    // 重置序列执行状态
    void resetSequenceExecution();

    // 文件操作
    bool saveActionsToJson(const QString& filePath);    // 保存为JSON
    bool loadActionsFromJson(const QString& filePath);  // 从JSON加载

    // 应用设置
    void saveSettings();               // 保存设置
    void loadSettings();               // 加载设置

    // UI 组件
    QSpinBox *xSpinBox;
    QSpinBox *ySpinBox;
    QDoubleSpinBox *delaySpinBox;
    QSpinBox *repeatCountSpinBox;
    QSpinBox *sequenceRepeatSpinBox;
    QSpinBox *countdownSpinBox;
    QPushButton *clickButton;
    QPushButton *pickCoordButton;
    QCheckBox *repeatCheckBox;
    QCheckBox *leftButtonCheck;
    QCheckBox *doubleClickCheck;
    QCheckBox *infiniteRepeatCheck;
    QCheckBox *infiniteSequenceRepeatCheck;
    QLabel *statusLabel;
    QLabel *cursorLabel;
    QProgressBar *progressBar;
    QLabel *progressLabel;

    // 动作序列相关UI组件
    QPushButton *addActionButton;
    QPushButton *removeActionButton;
    QPushButton *clearActionsButton;
    QPushButton *executeSequenceButton;
    QPushButton *repeatSequenceButton;
    QPushButton *stopSequenceButton;
    QPushButton *exportActionsButton;  // 导出按钮
    QPushButton *importActionsButton;  // 导入按钮
    QTableWidget *actionTable;

    // 右键菜单
    QMenu *tableContextMenu;

    QTimer *repeatTimer;
    QTimer *sequenceTimer;
    QTimer *countdownTimer;
    bool isRepeating;
    bool isSequenceRunning;
    bool isRepeatingSequence;
    bool isInfiniteSequenceRepeat;

    // 动作数据
    std::vector<ClickAction> actionList;
    int currentActionIndex;
    int nextActionId;
    int countdownSeconds;
    bool hotkeyRegistered;

    // 计数相关
    int currentRepeatCount;
    int totalRepeatCount;
    int currentSequenceRepeat;
    int totalSequenceRepeat;

    // 中断标志
    bool stopRequested;
};

#endif // MAINWINDOW_H

mainwindow.cpp

#include "mainwindow.h"
#include <QApplication>
#include <QDesktopWidget>
#include <QVBoxLayout>
#include <QHBoxLayout>
#include <QGridLayout>
#include <QGroupBox>
#include <QPushButton>
#include <QSpinBox>
#include <QDoubleSpinBox>
#include <QLabel>
#include <QCheckBox>
#include <QTimer>
#include <QMouseEvent>
#include <QDebug>
#include <QMessageBox>
#include <QTableWidget>
#include <QHeaderView>
#include <QFile>
#include <QTextStream>
#include <QKeyEvent>
#include <QShortcut>
#include <QFileDialog>
#include <QProgressBar>
#include <QFont>
#include <QResizeEvent>
#include <QJsonDocument>
#include <QJsonArray>
#include <QJsonObject>
#include <QDateTime>
#include <QMenu>
#include <QAction>
#include <QInputDialog>
#include <QComboBox>
#include <QIntValidator>
#include <QDoubleValidator>
#include <QItemDelegate>
#include <QStyleOptionComboBox>
#include <QSettings>

// 全局热键消息ID
#define HOTKEY_ID 1

// 自定义表格委托类 - 用于输入验证和下拉框
class ActionTableDelegate : public QItemDelegate
{
public:
    explicit ActionTableDelegate(QObject *parent = nullptr) : QItemDelegate(parent) {}

    QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option,
                          const QModelIndex &index) const override
    {
        switch (index.column()) {
        case 1: // X坐标 - 整数输入
        case 2: { // Y坐标 - 整数输入
            QSpinBox *editor = new QSpinBox(parent);
            editor->setRange(0, GetSystemMetrics(index.column() == 1 ? SM_CXSCREEN : SM_CYSCREEN) - 1);
            editor->setSuffix(" px");
            return editor;
        }
        case 3: { // 延迟时间 - 浮点数输入
            QDoubleSpinBox *editor = new QDoubleSpinBox(parent);
            editor->setRange(0.001, 10800.0);
            editor->setDecimals(3);
            editor->setSingleStep(0.1);
            editor->setSuffix(" 秒");
            return editor;
        }
        case 4: { // 按键选择 - 下拉框
            QComboBox *editor = new QComboBox(parent);
            editor->addItem("左键");
            editor->addItem("右键");
            return editor;
        }
        case 5: { // 双击选择 - 下拉框
            QComboBox *editor = new QComboBox(parent);
            editor->addItem("是");
            editor->addItem("否");
            return editor;
        }
        default:
            return QItemDelegate::createEditor(parent, option, index);
        }
    }

    void setEditorData(QWidget *editor, const QModelIndex &index) const override
    {
        QString value = index.model()->data(index, Qt::EditRole).toString();

        switch (index.column()) {
        case 1: // X坐标
        case 2: { // Y坐标
            QSpinBox *spinBox = static_cast<QSpinBox*>(editor);
            spinBox->setValue(value.toInt());
            break;
        }
        case 3: { // 延迟时间
            QDoubleSpinBox *spinBox = static_cast<QDoubleSpinBox*>(editor);
            spinBox->setValue(value.toDouble());
            break;
        }
        case 4: { // 按键选择
            QComboBox *comboBox = static_cast<QComboBox*>(editor);
            comboBox->setCurrentText(value);
            break;
        }
        case 5: { // 双击选择
            QComboBox *comboBox = static_cast<QComboBox*>(editor);
            comboBox->setCurrentText(value);
            break;
        }
        default:
            QItemDelegate::setEditorData(editor, index);
        }
    }

    void setModelData(QWidget *editor, QAbstractItemModel *model,
                      const QModelIndex &index) const override
    {
        switch (index.column()) {
        case 1: // X坐标
        case 2: { // Y坐标
            QSpinBox *spinBox = static_cast<QSpinBox*>(editor);
            model->setData(index, spinBox->value(), Qt::EditRole);
            break;
        }
        case 3: { // 延迟时间
            QDoubleSpinBox *spinBox = static_cast<QDoubleSpinBox*>(editor);
            model->setData(index, spinBox->value(), Qt::EditRole);
            break;
        }
        case 4: // 按键选择
        case 5: { // 双击选择
            QComboBox *comboBox = static_cast<QComboBox*>(editor);
            model->setData(index, comboBox->currentText(), Qt::EditRole);
            break;
        }
        default:
            QItemDelegate::setModelData(editor, model, index);
        }
    }
};

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , isRepeating(false)
    , isSequenceRunning(false)
    , isRepeatingSequence(false)
    , isInfiniteSequenceRepeat(false)
    , currentActionIndex(0)
    , nextActionId(1)
    , stopRequested(false)
    , countdownSeconds(0)
    , currentRepeatCount(0)
    , totalRepeatCount(0)
    , currentSequenceRepeat(0)
    , totalSequenceRepeat(0)
    , hotkeyRegistered(false)
    , tableContextMenu(nullptr)
{
    // 设置窗口
    setWindowTitle("智能鼠标点击器 v2.0 - 支持动作序列和重复执行");
    resize(900, 750);  // 增大窗口尺寸

    // 设置窗口图标和样式
    setStyleSheet("QMainWindow { background-color: #f0f0f0; }");

    // 安装事件过滤器
    qApp->installEventFilter(this);

    // 创建中央部件
    QWidget *centralWidget = new QWidget(this);
    centralWidget->setStyleSheet("QWidget { background-color: white; border-radius: 10px; }");
    setCentralWidget(centralWidget);

    // 创建主布局
    QVBoxLayout *mainLayout = new QVBoxLayout(centralWidget);
    mainLayout->setSpacing(15);  // 增加间距
    mainLayout->setContentsMargins(15, 15, 15, 15);  // 增加边距

    // ========== 基础点击控制区域 ==========
    QGroupBox *basicGroup = new QGroupBox("🎯 基础点击控制", centralWidget);
    basicGroup->setStyleSheet(
        "QGroupBox {"
        "  font-weight: bold;"
        "  font-size: 14px;"
        "  border: 2px solid #4CAF50;"
        "  border-radius: 10px;"
        "  margin-top: 10px;"
        "  padding-top: 15px;"  // 增加内边距
        "}"
        "QGroupBox::title {"
        "  subcontrol-origin: margin;"
        "  left: 15px;"  // 增加左边距
        "  padding: 0 10px 0 10px;"  // 增加内边距
        "  color: #4CAF50;"
        "  font-size: 14px;"
        "}"
    );

    QGridLayout *basicLayout = new QGridLayout(basicGroup);
    basicLayout->setVerticalSpacing(12);  // 增加垂直间距
    basicLayout->setHorizontalSpacing(15);  // 增加水平间距
    basicLayout->setContentsMargins(15, 15, 15, 15);  // 增加内边距

    // X坐标
    QLabel *xLabel = new QLabel("📌 X坐标:", basicGroup);
    xLabel->setToolTip("点击位置的X坐标 (0-" + QString::number(GetSystemMetrics(SM_CXSCREEN) - 1) + ")");
    xLabel->setStyleSheet("QLabel { font-size: 13px; font-weight: 500; }");
    basicLayout->addWidget(xLabel, 0, 0);

    xSpinBox = new QSpinBox(basicGroup);
    xSpinBox->setRange(0, GetSystemMetrics(SM_CXSCREEN) - 1);
    xSpinBox->setValue(100);
    xSpinBox->setSuffix(" px");
    xSpinBox->setToolTip("设置点击位置的X坐标\n按空格键可快速拾取当前光标位置");
    xSpinBox->setStyleSheet(
        "QSpinBox {"
        "  padding: 6px;"
        "  font-size: 13px;"
        "  border: 1px solid #ccc;"
        "  border-radius: 4px;"
        "}"
        "QSpinBox:hover {"
        "  border: 1px solid #4CAF50;"
        "}"
    );
    basicLayout->addWidget(xSpinBox, 0, 1);

    // Y坐标
    QLabel *yLabel = new QLabel("📌 Y坐标:", basicGroup);
    yLabel->setToolTip("点击位置的Y坐标 (0-" + QString::number(GetSystemMetrics(SM_CYSCREEN) - 1) + ")");
    yLabel->setStyleSheet("QLabel { font-size: 13px; font-weight: 500; }");
    basicLayout->addWidget(yLabel, 1, 0);

    ySpinBox = new QSpinBox(basicGroup);
    ySpinBox->setRange(0, GetSystemMetrics(SM_CYSCREEN) - 1);
    ySpinBox->setValue(100);
    ySpinBox->setSuffix(" px");
    ySpinBox->setToolTip("设置点击位置的Y坐标\n按空格键可快速拾取当前光标位置");
    ySpinBox->setStyleSheet(
        "QSpinBox {"
        "  padding: 6px;"
        "  font-size: 13px;"
        "  border: 1px solid #ccc;"
        "  border-radius: 4px;"
        "}"
        "QSpinBox:hover {"
        "  border: 1px solid #4CAF50;"
        "}"
    );
    basicLayout->addWidget(ySpinBox, 1, 1);

    // 延迟时间
    QLabel *delayLabel = new QLabel("⏱️ 延迟时间:", basicGroup);
    delayLabel->setToolTip("每次点击之间的间隔时间");
    delayLabel->setStyleSheet("QLabel { font-size: 13px; font-weight: 500; }");
    basicLayout->addWidget(delayLabel, 2, 0);

    delaySpinBox = new QDoubleSpinBox(basicGroup);
    delaySpinBox->setRange(0.001, 10800.0);
    delaySpinBox->setValue(1.0);
    delaySpinBox->setDecimals(3);
    delaySpinBox->setSingleStep(0.1);
    delaySpinBox->setSuffix(" 秒");
    delaySpinBox->setToolTip("重复点击时的间隔时间\n范围: 0.001秒 到 3小时");
    delaySpinBox->setStyleSheet(
        "QDoubleSpinBox {"
        "  padding: 6px;"
        "  font-size: 13px;"
        "  border: 1px solid #ccc;"
        "  border-radius: 4px;"
        "}"
        "QDoubleSpinBox:hover {"
        "  border: 1px solid #4CAF50;"
        "}"
    );
    basicLayout->addWidget(delaySpinBox, 2, 1);

    // 倒计时设置
    QLabel *countdownLabel = new QLabel("⏱️ 开始倒计时:", basicGroup);
    countdownLabel->setToolTip("任务开始前的倒计时时间");
    countdownLabel->setStyleSheet("QLabel { font-size: 13px; font-weight: 500; }");
    basicLayout->addWidget(countdownLabel, 3, 0);

    countdownSpinBox = new QSpinBox(basicGroup);
    countdownSpinBox->setRange(0, 10800);
    countdownSpinBox->setValue(3);
    countdownSpinBox->setSuffix(" 秒");
    countdownSpinBox->setToolTip("任务开始前的倒计时时间,设为0则不显示倒计时");
    countdownSpinBox->setStyleSheet(
        "QSpinBox {"
        "  padding: 6px;"
        "  font-size: 13px;"
        "  border: 1px solid #ccc;"
        "  border-radius: 4px;"
        "}"
        "QSpinBox:hover {"
        "  border: 1px solid #4CAF50;"
        "}"
    );
    basicLayout->addWidget(countdownSpinBox, 3, 1);

    // 拾取坐标按钮
    pickCoordButton = new QPushButton("🎯 拾取坐标", basicGroup);
    pickCoordButton->setToolTip("点击后移动鼠标到目标位置,按空格键拾取坐标");
    pickCoordButton->setStyleSheet(
        "QPushButton {"
        "  background-color: #FF9800;"
        "  color: white;"
        "  font-weight: bold;"
        "  font-size: 13px;"
        "  padding: 8px 16px;"
        "  border-radius: 6px;"
        "  border: none;"
        "}"
        "QPushButton:hover {"
        "  background-color: #F57C00;"
        "}"
        "QPushButton:pressed {"
        "  background-color: #E65100;"
        "}"
    );
    basicLayout->addWidget(pickCoordButton, 0, 2, 2, 1);

    // 立即点击按钮
    clickButton = new QPushButton("🎯 立即点击", basicGroup);
    clickButton->setStyleSheet(
        "QPushButton {"
        "  background-color: #2196F3;"
        "  color: white;"
        "  font-size: 14px;"
        "  font-weight: bold;"
        "  padding: 10px;"
        "  border-radius: 6px;"
        "  border: none;"
        "  min-width: 140px;"
        "}"
        "QPushButton:hover {"
        "  background-color: #1976D2;"
        "}"
        "QPushButton:pressed {"
        "  background-color: #0D47A1;"
        "}"
        "QPushButton:disabled {"
        "  background-color: #BDBDBD;"
        "}"
    );
    clickButton->setToolTip("重复点击启用时:开始/停止重复点击\n重复点击未启用时:执行单次点击\n快捷键: Ctrl+S");
    basicLayout->addWidget(clickButton, 2, 2, 2, 1);

    // 光标位置显示
    cursorLabel = new QLabel("🖱️ 光标位置: (0, 0)", basicGroup);
    cursorLabel->setStyleSheet(
        "QLabel {"
        "  color: #0066CC;"
        "  font-style: italic;"
        "  font-size: 13px;"
        "  font-weight: bold;"
        "  background-color: #f0f8ff;"
        "  padding: 8px 12px;"
        "  border-radius: 8px;"
        "  border: 1px solid #b3e0ff;"
        "}"
    );
    basicLayout->addWidget(cursorLabel, 4, 0, 1, 3);

    // 点击选项
    QHBoxLayout *optionsLayout = new QHBoxLayout();
    optionsLayout->setSpacing(20);  // 增加间距

    leftButtonCheck = new QCheckBox("🖱️ 左键点击", basicGroup);
    leftButtonCheck->setChecked(true);
    leftButtonCheck->setToolTip("使用鼠标左键点击\n取消勾选将使用右键点击");
    leftButtonCheck->setStyleSheet("QCheckBox { font-size: 13px; font-weight: 500; }");
    optionsLayout->addWidget(leftButtonCheck);

    doubleClickCheck = new QCheckBox("⚡ 双击", basicGroup);
    doubleClickCheck->setToolTip("执行双击操作而不是单击");
    doubleClickCheck->setStyleSheet("QCheckBox { font-size: 13px; font-weight: 500; }");
    optionsLayout->addWidget(doubleClickCheck);

    optionsLayout->addStretch();
    basicLayout->addLayout(optionsLayout, 5, 0, 1, 3);

    // 重复点击设置
    QHBoxLayout *repeatSettingsLayout = new QHBoxLayout();
    repeatSettingsLayout->setSpacing(10);  // 增加间距

    repeatCheckBox = new QCheckBox("🔁 启用重复点击", basicGroup);
    repeatCheckBox->setToolTip("勾选后可以设置重复点击次数和间隔");
    repeatCheckBox->setChecked(false);
    repeatCheckBox->setStyleSheet("QCheckBox { font-size: 13px; font-weight: 500; }");
    repeatSettingsLayout->addWidget(repeatCheckBox);
    connect(repeatCheckBox, &QCheckBox::stateChanged, this, &MainWindow::onRepeatStateChanged);

    QLabel *repeatCountLabel = new QLabel("次数:", basicGroup);
    repeatCountLabel->setStyleSheet("QLabel { font-size: 13px; font-weight: 500; }");
    repeatSettingsLayout->addWidget(repeatCountLabel);

    repeatCountSpinBox = new QSpinBox(basicGroup);
    repeatCountSpinBox->setRange(1, 9999);
    repeatCountSpinBox->setValue(10);
    repeatCountSpinBox->setSuffix(" 次");
    repeatCountSpinBox->setToolTip("重复点击的次数\n设置为1时相当于单次点击");
    repeatCountSpinBox->setEnabled(false);
    repeatCountSpinBox->setStyleSheet(
        "QSpinBox {"
        "  font-size: 13px;"
        "  padding: 5px;"
        "  border: 1px solid #ccc;"
        "  border-radius: 4px;"
        "}"
    );
    repeatSettingsLayout->addWidget(repeatCountSpinBox);
    connect(repeatCountSpinBox, QOverload<int>::of(&QSpinBox::valueChanged),
            this, &MainWindow::onRepeatCountChanged);

    infiniteRepeatCheck = new QCheckBox("无限循环", basicGroup);
    infiniteRepeatCheck->setToolTip("勾选后将无限重复点击,直到手动停止");
    infiniteRepeatCheck->setEnabled(false);
    infiniteRepeatCheck->setStyleSheet("QCheckBox { font-size: 13px; font-weight: 500; }");
    repeatSettingsLayout->addWidget(infiniteRepeatCheck);
    connect(infiniteRepeatCheck, &QCheckBox::stateChanged, this, [this](int state) {
        repeatCountSpinBox->setEnabled(state == Qt::Unchecked);
    });

    repeatSettingsLayout->addStretch();
    basicLayout->addLayout(repeatSettingsLayout, 6, 0, 1, 3);

    // 连接按钮信号
    connect(pickCoordButton, &QPushButton::clicked, this, &MainWindow::onPickCoordClicked);
    connect(clickButton, &QPushButton::clicked, this, &MainWindow::onClickButtonClicked);

    mainLayout->addWidget(basicGroup);

    // ========== 动作序列区域 ==========
    QGroupBox *sequenceGroup = new QGroupBox("📋 动作序列管理", centralWidget);
    sequenceGroup->setStyleSheet(
        "QGroupBox {"
        "  font-weight: bold;"
        "  font-size: 14px;"
        "  border: 2px solid #2196F3;"
        "  border-radius: 10px;"
        "  margin-top: 10px;"
        "  padding-top: 15px;"
        "}"
        "QGroupBox::title {"
        "  subcontrol-origin: margin;"
        "  left: 15px;"
        "  padding: 0 10px 0 10px;"
        "  color: #2196F3;"
        "  font-size: 14px;"
        "}"
    );

    QVBoxLayout *sequenceLayout = new QVBoxLayout(sequenceGroup);
    sequenceLayout->setSpacing(10);  // 增加间距
    sequenceLayout->setContentsMargins(15, 15, 15, 15);  // 增加内边距

    // 动作表格
    actionTable = new QTableWidget(sequenceGroup);
    actionTable->setColumnCount(6);
    QStringList headers;
    headers << "ID" << "X坐标" << "Y坐标" << "延迟(秒)" << "按键" << "双击";
    actionTable->setHorizontalHeaderLabels(headers);
    actionTable->setSelectionBehavior(QTableWidget::SelectRows);
    actionTable->setSelectionMode(QTableWidget::SingleSelection);
    actionTable->setEditTriggers(QTableWidget::DoubleClicked | QTableWidget::EditKeyPressed);
    actionTable->setMinimumHeight(180);
    actionTable->setMaximumHeight(300);
    actionTable->setContextMenuPolicy(Qt::CustomContextMenu);  // 启用右键菜单

    actionTable->setStyleSheet(
        "QTableWidget {"
        "  background-color: white;"
        "  alternate-background-color: #f8fafc;"
        "  border: 1px solid #e1e4e8;"
        "  border-radius: 8px;"
        "  padding: 1px;"
        "  gridline-color: #eef2f7;"
        "  font-size: 12px;"
        "  outline: 0;"
        "}"
        "QTableWidget::item {"
        "  padding: 6px 4px;"
        "  border-bottom: 1px solid #f0f0f0;"
        "  color: #333333;"
        "}"
        "QTableWidget::item:selected {"
        "  background-color: rgba(25, 118, 210, 0.2);"
        "  color: #1976D2;"
        "  font-weight: bold;"
        "  border: 1px solid #1976D2;"
        "  border-radius: 4px;"
        "}"
        "QTableWidget::item:hover:!selected {"
        "  background-color: #f8fafc;"
        "}"
        "QHeaderView::section {"
        "  background-color: #2196F3;"
        "  color: white;"
        "  padding: 10px 4px;"
        "  font-weight: bold;"
        "  font-size: 13px;"
        "  border: none;"
        "  border-right: 1px solid #1976D2;"
        "}"
    );

    actionTable->setAlternatingRowColors(true);
    QHeaderView* header = actionTable->horizontalHeader();
    header->setStretchLastSection(true);
    header->setDefaultAlignment(Qt::AlignCenter | Qt::AlignVCenter);
    header->setSectionResizeMode(QHeaderView::Interactive);
    header->setMinimumSectionSize(60);
    header->setDefaultSectionSize(100);

    // 设置列宽
    actionTable->setColumnWidth(0, 60);    // ID
    actionTable->setColumnWidth(1, 100);   // X坐标
    actionTable->setColumnWidth(2, 100);   // Y坐标
    actionTable->setColumnWidth(3, 120);   // 延迟(秒)
    actionTable->setColumnWidth(4, 100);   // 按键
    actionTable->setColumnWidth(5, 100);   // 双击

    actionTable->verticalHeader()->setDefaultSectionSize(35);
    actionTable->verticalHeader()->setVisible(false);
    actionTable->setShowGrid(false);
    actionTable->setCornerButtonEnabled(false);

    // 设置自定义委托
    actionTable->setItemDelegate(new ActionTableDelegate(this));

    sequenceLayout->addWidget(actionTable, 1);

    // 连接表格信号
    connect(actionTable, &QTableWidget::itemChanged, this, &MainWindow::onActionTableItemChanged);
    connect(actionTable, &QTableWidget::itemSelectionChanged, this, &MainWindow::onActionTableSelectionChanged);
    connect(actionTable, &QTableWidget::customContextMenuRequested, this, &MainWindow::onTableContextMenu);

    // 序列重复设置
    QHBoxLayout *repeatSequenceLayout = new QHBoxLayout();
    repeatSequenceLayout->setSpacing(10);

    QLabel *seqRepeatLabel = new QLabel("序列重复次数:", sequenceGroup);
    seqRepeatLabel->setStyleSheet("QLabel { font-size: 13px; font-weight: 500; }");
    repeatSequenceLayout->addWidget(seqRepeatLabel);

    sequenceRepeatSpinBox = new QSpinBox(sequenceGroup);
    sequenceRepeatSpinBox->setRange(1, 9999);
    sequenceRepeatSpinBox->setValue(1);
    sequenceRepeatSpinBox->setSuffix(" 次");
    sequenceRepeatSpinBox->setToolTip("序列重复执行的次数");
    sequenceRepeatSpinBox->setStyleSheet(
        "QSpinBox {"
        "  font-size: 13px;"
        "  padding: 5px;"
        "  border: 1px solid #ccc;"
        "  border-radius: 4px;"
        "}"
    );
    repeatSequenceLayout->addWidget(sequenceRepeatSpinBox);

    infiniteSequenceRepeatCheck = new QCheckBox("无限循环", sequenceGroup);
    infiniteSequenceRepeatCheck->setToolTip("勾选后将无限重复执行序列,直到手动停止");
    infiniteSequenceRepeatCheck->setStyleSheet("QCheckBox { font-size: 13px; font-weight: 500; }");
    repeatSequenceLayout->addWidget(infiniteSequenceRepeatCheck);
    connect(infiniteSequenceRepeatCheck, &QCheckBox::stateChanged, this, &MainWindow::onInfiniteSequenceRepeatChanged);

    repeatSequenceLayout->addStretch();
    sequenceLayout->addLayout(repeatSequenceLayout);

    // 序列控制按钮 - 第一行
    QHBoxLayout *sequenceBtnLayout1 = new QHBoxLayout();
    sequenceBtnLayout1->setSpacing(8);

    addActionButton = new QPushButton("➕ 添加动作", sequenceGroup);
    addActionButton->setToolTip("将当前设置的点击参数添加到序列中\n快捷键: Ctrl+A");
    addActionButton->setStyleSheet(
        "QPushButton {"
        "  background-color: #4CAF50;"
        "  color: white;"
        "  font-size: 13px;"
        "  font-weight: 500;"
        "  padding: 8px 16px;"
        "  border-radius: 6px;"
        "  border: none;"
        "}"
        "QPushButton:hover {"
        "  background-color: #388E3C;"
        "}"
        "QPushButton:pressed {"
        "  background-color: #2E7D32;"
        "}"
    );
    sequenceBtnLayout1->addWidget(addActionButton);
    connect(addActionButton, &QPushButton::clicked, this, &MainWindow::onAddActionClicked);

    removeActionButton = new QPushButton("➖ 移除动作", sequenceGroup);
    removeActionButton->setToolTip("移除选中的动作\n快捷键: Ctrl+D");
    removeActionButton->setStyleSheet(
        "QPushButton {"
        "  background-color: #FF9800;"
        "  color: white;"
        "  font-size: 13px;"
        "  font-weight: 500;"
        "  padding: 8px 16px;"
        "  border-radius: 6px;"
        "  border: none;"
        "}"
        "QPushButton:hover {"
        "  background-color: #F57C00;"
        "}"
        "QPushButton:pressed {"
        "  background-color: #E65100;"
        "}"
        "QPushButton:disabled {"
        "  background-color: #FFCC80;"
        "  color: #666;"
        "}"
    );
    removeActionButton->setEnabled(false);
    sequenceBtnLayout1->addWidget(removeActionButton);
    connect(removeActionButton, &QPushButton::clicked, this, &MainWindow::onRemoveActionClicked);

    clearActionsButton = new QPushButton("🗑️ 清空动作", sequenceGroup);
    clearActionsButton->setToolTip("清空所有动作");
    clearActionsButton->setStyleSheet(
        "QPushButton {"
        "  background-color: #F44336;"
        "  color: white;"
        "  font-size: 13px;"
        "  font-weight: 500;"
        "  padding: 8px 16px;"
        "  border-radius: 6px;"
        "  border: none;"
        "}"
        "QPushButton:hover {"
        "  background-color: #D32F2F;"
        "}"
        "QPushButton:pressed {"
        "  background-color: #B71C1C;"
        "}"
    );
    sequenceBtnLayout1->addWidget(clearActionsButton);
    connect(clearActionsButton, &QPushButton::clicked, this, &MainWindow::onClearActionsClicked);

    sequenceLayout->addLayout(sequenceBtnLayout1);

    // 序列控制按钮 - 第二行
    QHBoxLayout *sequenceBtnLayout2 = new QHBoxLayout();
    sequenceBtnLayout2->setSpacing(8);

    importActionsButton = new QPushButton("📥 导入序列", sequenceGroup);
    importActionsButton->setToolTip("从JSON文件导入动作序列");
    importActionsButton->setStyleSheet(
        "QPushButton {"
        "  background-color: #673AB7;"
        "  color: white;"
        "  font-size: 13px;"
        "  font-weight: 500;"
        "  padding: 8px 16px;"
        "  border-radius: 6px;"
        "  border: none;"
        "}"
        "QPushButton:hover {"
        "  background-color: #5E35B1;"
        "}"
        "QPushButton:pressed {"
        "  background-color: #512DA8;"
        "}"
    );
    sequenceBtnLayout2->addWidget(importActionsButton);
    connect(importActionsButton, &QPushButton::clicked, this, &MainWindow::onImportActionsClicked);

    exportActionsButton = new QPushButton("📤 导出序列", sequenceGroup);
    exportActionsButton->setToolTip("将动作序列导出为JSON文件");
    exportActionsButton->setStyleSheet(
        "QPushButton {"
        "  background-color: #009688;"
        "  color: white;"
        "  font-size: 13px;"
        "  font-weight: 500;"
        "  padding: 8px 16px;"
        "  border-radius: 6px;"
        "  border: none;"
        "}"
        "QPushButton:hover {"
        "  background-color: #00897B;"
        "}"
        "QPushButton:pressed {"
        "  background-color: #00695C;"
        "}"
    );
    sequenceBtnLayout2->addWidget(exportActionsButton);
    connect(exportActionsButton, &QPushButton::clicked, this, &MainWindow::onExportActionsClicked);

    executeSequenceButton = new QPushButton("▶️ 执行序列", sequenceGroup);
    executeSequenceButton->setToolTip("执行动作序列一次\n快捷键: Ctrl+S");
    executeSequenceButton->setStyleSheet(
        "QPushButton {"
        "  background-color: #2196F3;"
        "  color: white;"
        "  font-weight: 600;"
        "  font-size: 13px;"
        "  padding: 8px 20px;"
        "  border-radius: 6px;"
        "  border: none;"
        "}"
        "QPushButton:hover {"
        "  background-color: #1976D2;"
        "}"
        "QPushButton:pressed {"
        "  background-color: #0D47A1;"
        "}"
    );
    sequenceBtnLayout2->addWidget(executeSequenceButton);
    connect(executeSequenceButton, &QPushButton::clicked, this, &MainWindow::onExecuteSequenceClicked);

    repeatSequenceButton = new QPushButton("🔁 重复执行序列", sequenceGroup);
    repeatSequenceButton->setToolTip("重复执行动作序列\n快捷键: Ctrl+R");
    repeatSequenceButton->setStyleSheet(
        "QPushButton {"
        "  background-color: #9C27B0;"
        "  color: white;"
        "  font-weight: 600;"
        "  font-size: 13px;"
        "  padding: 8px 20px;"
        "  border-radius: 6px;"
        "  border: none;"
        "}"
        "QPushButton:hover {"
        "  background-color: #7B1FA2;"
        "}"
        "QPushButton:pressed {"
        "  background-color: #4A148C;"
        "}"
    );
    sequenceBtnLayout2->addWidget(repeatSequenceButton);
    connect(repeatSequenceButton, &QPushButton::clicked, this, &MainWindow::onRepeatSequenceClicked);

    stopSequenceButton = new QPushButton("⏹️ 停止序列", sequenceGroup);
    stopSequenceButton->setToolTip("停止正在执行的序列\n快捷键: Ctrl+C 或 Esc");
    stopSequenceButton->setStyleSheet(
        "QPushButton {"
        "  background-color: #757575;"
        "  color: white;"
        "  font-size: 13px;"
        "  font-weight: 500;"
        "  padding: 8px 20px;"
        "  border-radius: 6px;"
        "  border: none;"
        "}"
        "QPushButton:hover {"
        "  background-color: #616161;"
        "}"
        "QPushButton:pressed {"
        "  background-color: #424242;"
        "}"
        "QPushButton:disabled {"
        "  background-color: #BDBDBD;"
        "  color: #999;"
        "}"
    );
    stopSequenceButton->setEnabled(false);
    sequenceBtnLayout2->addWidget(stopSequenceButton);
    connect(stopSequenceButton, &QPushButton::clicked, this, &MainWindow::onStopSequenceClicked);

    sequenceLayout->addLayout(sequenceBtnLayout2);

    mainLayout->addWidget(sequenceGroup, 1);

    // ========== 进度显示区域 ==========
    QHBoxLayout *progressLayout = new QHBoxLayout();

    progressLabel = new QLabel("就绪", centralWidget);
    progressLabel->setAlignment(Qt::AlignLeft);
    progressLabel->setStyleSheet(
        "QLabel {"
        "  color: #333;"
        "  padding: 8px;"
        "  min-height: 25px;"
        "  font-size: 13px;"
        "  font-weight: 500;"
        "}"
    );

    progressBar = new QProgressBar(centralWidget);
    progressBar->setRange(0, 100);
    progressBar->setValue(0);
    progressBar->setTextVisible(true);
    progressBar->setFormat("%p%");
    progressBar->setStyleSheet(
        "QProgressBar {"
        "  border: 1px solid #ddd;"
        "  border-radius: 8px;"
        "  text-align: center;"
        "  height: 24px;"
        "  font-size: 12px;"
        "  font-weight: 500;"
        "}"
        "QProgressBar::chunk {"
        "  background-color: #4CAF50;"
        "  border-radius: 7px;"
        "  border: none;"
        "}"
    );

    progressLayout->addWidget(progressLabel, 1);
    progressLayout->addWidget(progressBar, 2);
    mainLayout->addLayout(progressLayout);

    // ========== 状态显示 ==========
    statusLabel = new QLabel("✅ 就绪 - 欢迎使用智能鼠标点击器 v2.0!", centralWidget);
    statusLabel->setAlignment(Qt::AlignCenter);
    statusLabel->setStyleSheet(
        "QLabel {"
        "  background-color: #e8f5e9;"
        "  color: #2e7d32;"
        "  border: 2px solid #c8e6c9;"
        "  padding: 10px;"
        "  border-radius: 10px;"
        "  font-weight: 600;"
        "  font-size: 13px;"
        "}"
    );
    mainLayout->addWidget(statusLabel);

    mainLayout->addStretch();

    // ========== 初始化定时器 ==========
    repeatTimer = new QTimer(this);
    connect(repeatTimer, &QTimer::timeout, this, &MainWindow::onRepeatTimeout);

    sequenceTimer = new QTimer(this);
    sequenceTimer->setSingleShot(true);
    connect(sequenceTimer, &QTimer::timeout, this, &MainWindow::onSequenceTimeout);

    countdownTimer = new QTimer(this);
    countdownTimer->setInterval(1000);
    connect(countdownTimer, &QTimer::timeout, this, &MainWindow::onCountdownTimeout);

    // 定时更新光标位置
    QTimer *cursorTimer = new QTimer(this);
    connect(cursorTimer, &QTimer::timeout, [this]() {
        POINT cursorPos;
        GetCursorPos(&cursorPos);
        cursorLabel->setText(QString("🖱️ 光标位置: (%1, %2)").arg(cursorPos.x).arg(cursorPos.y));
    });
    cursorTimer->start(100);

    // 创建表格右键菜单
    createTableContextMenu();

    // 设置快捷键
    setupShortcuts();

    // 加载保存的设置
    loadSettings();
}

MainWindow::~MainWindow()
{
    // 保存设置
    saveSettings();

    if (hotkeyRegistered) {
        UnregisterHotKey((HWND)winId(), HOTKEY_ID);
    }

    if (tableContextMenu) {
        delete tableContextMenu;
    }
}

// 保存设置
void MainWindow::saveSettings()
{
    QSettings settings("SmartClicker", "MouseClicker");

    // 保存窗口位置和大小
    settings.setValue("window/geometry", saveGeometry());

    // 保存基础设置
    settings.setValue("basic/x", xSpinBox->value());
    settings.setValue("basic/y", ySpinBox->value());
    settings.setValue("basic/delay", delaySpinBox->value());
    settings.setValue("basic/countdown", countdownSpinBox->value());
    settings.setValue("basic/leftButton", leftButtonCheck->isChecked());
    settings.setValue("basic/doubleClick", doubleClickCheck->isChecked());
    settings.setValue("basic/repeatEnabled", repeatCheckBox->isChecked());
    settings.setValue("basic/repeatCount", repeatCountSpinBox->value());
    settings.setValue("basic/infiniteRepeat", infiniteRepeatCheck->isChecked());

    // 保存序列设置
    settings.setValue("sequence/repeatCount", sequenceRepeatSpinBox->value());
    settings.setValue("sequence/infiniteRepeat", infiniteSequenceRepeatCheck->isChecked());
}

// 加载设置
void MainWindow::loadSettings()
{
    QSettings settings("SmartClicker", "MouseClicker");

    // 恢复窗口位置和大小
    restoreGeometry(settings.value("window/geometry").toByteArray());

    // 恢复基础设置
    xSpinBox->setValue(settings.value("basic/x", 100).toInt());
    ySpinBox->setValue(settings.value("basic/y", 100).toInt());
    delaySpinBox->setValue(settings.value("basic/delay", 1.0).toDouble());
    countdownSpinBox->setValue(settings.value("basic/countdown", 3).toInt());
    leftButtonCheck->setChecked(settings.value("basic/leftButton", true).toBool());
    doubleClickCheck->setChecked(settings.value("basic/doubleClick", false).toBool());
    repeatCheckBox->setChecked(settings.value("basic/repeatEnabled", false).toBool());
    repeatCountSpinBox->setValue(settings.value("basic/repeatCount", 10).toInt());
    infiniteRepeatCheck->setChecked(settings.value("basic/infiniteRepeat", false).toBool());

    // 恢复序列设置
    sequenceRepeatSpinBox->setValue(settings.value("sequence/repeatCount", 1).toInt());
    infiniteSequenceRepeatCheck->setChecked(settings.value("sequence/infiniteRepeat", false).toBool());

    // 更新按钮状态
    onRepeatStateChanged(repeatCheckBox->isChecked() ? Qt::Checked : Qt::Unchecked);
}

// 创建表格右键菜单
void MainWindow::createTableContextMenu()
{
    tableContextMenu = new QMenu(this);

    QAction *insertAction = new QAction("📝 插入动作", this);
    insertAction->setToolTip("在当前选中行之前插入新动作");
    connect(insertAction, &QAction::triggered, this, &MainWindow::onInsertAction);

    QAction *duplicateAction = new QAction("📋 复制动作", this);
    duplicateAction->setToolTip("复制选中的动作");
    connect(duplicateAction, &QAction::triggered, this, &MainWindow::onDuplicateAction);

    QAction *moveUpAction = new QAction("⬆️ 上移", this);
    moveUpAction->setToolTip("将选中的动作上移一位");
    connect(moveUpAction, &QAction::triggered, this, &MainWindow::onMoveActionUp);

    QAction *moveDownAction = new QAction("⬇️ 下移", this);
    moveDownAction->setToolTip("将选中的动作下移一位");
    connect(moveDownAction, &QAction::triggered, this, &MainWindow::onMoveActionDown);

    tableContextMenu->addAction(insertAction);
    tableContextMenu->addAction(duplicateAction);
    tableContextMenu->addSeparator();
    tableContextMenu->addAction(moveUpAction);
    tableContextMenu->addAction(moveDownAction);
}

// 表格右键菜单事件
void MainWindow::onTableContextMenu(const QPoint &pos)
{
    QTableWidgetItem *item = actionTable->itemAt(pos);
    if (item) {
        tableContextMenu->exec(actionTable->viewport()->mapToGlobal(pos));
    }
}

// 插入动作
void MainWindow::onInsertAction()
{
    int currentRow = actionTable->currentRow();
    if (currentRow < 0) return;

    int x = xSpinBox->value();
    int y = ySpinBox->value();
    double delaySeconds = delaySpinBox->value();
    bool leftButton = leftButtonCheck->isChecked();
    bool doubleClick = doubleClickCheck->isChecked();

    ClickAction newAction(nextActionId++, x, y, delaySeconds, leftButton, doubleClick);
    actionList.insert(actionList.begin() + currentRow, newAction);
    updateActionTable();

    showStatusMessage(QString("✅ 已在位置 %1 插入动作 %2").arg(currentRow + 1).arg(newAction.id), false, true);
}

// 复制动作
void MainWindow::onDuplicateAction()
{
    int currentRow = actionTable->currentRow();
    if (currentRow < 0) return;

    if (currentRow >= 0 && currentRow < (int)actionList.size()) {
        ClickAction original = actionList[currentRow];
        ClickAction duplicate(nextActionId++, original.x, original.y, original.delay,
                             original.leftButton, original.doubleClick);

        actionList.insert(actionList.begin() + currentRow + 1, duplicate);
        updateActionTable();

        showStatusMessage(QString("✅ 已复制动作 %1 为动作 %2").arg(original.id).arg(duplicate.id), false, true);
    }
}

// 上移动作
void MainWindow::onMoveActionUp()
{
    int currentRow = actionTable->currentRow();
    if (currentRow > 0 && currentRow < (int)actionList.size()) {
        std::swap(actionList[currentRow], actionList[currentRow - 1]);
        updateActionTable();
        actionTable->selectRow(currentRow - 1);
        showStatusMessage(QString("✅ 已将动作上移一位"), false, true);
    }
}

// 下移动作
void MainWindow::onMoveActionDown()
{
    int currentRow = actionTable->currentRow();
    if (currentRow >= 0 && currentRow < (int)actionList.size() - 1) {
        std::swap(actionList[currentRow], actionList[currentRow + 1]);
        updateActionTable();
        actionTable->selectRow(currentRow + 1);
        showStatusMessage(QString("✅ 已将动作下移一位"), false, true);
    }
}

void MainWindow::registerGlobalHotkey()
{
    if (!hotkeyRegistered) {
        if (RegisterHotKey((HWND)winId(), HOTKEY_ID, MOD_CONTROL, 'C')) {
            hotkeyRegistered = true;
            qDebug() << "全局热键 Ctrl+C 已注册";
        } else {
            qDebug() << "全局热键注册失败";
        }
    }
}

void MainWindow::unregisterGlobalHotkey()
{
    if (hotkeyRegistered) {
        UnregisterHotKey((HWND)winId(), HOTKEY_ID);
        hotkeyRegistered = false;
        qDebug() << "全局热键 Ctrl+C 已注销";
    }
}

void MainWindow::resetSequenceExecution()
{
    currentActionIndex = 0;
    currentSequenceRepeat = 0;
    isSequenceRunning = false;
    isRepeatingSequence = false;
    isInfiniteSequenceRepeat = false;
    stopRequested = false;
}

void MainWindow::resizeEvent(QResizeEvent *event)
{
    QMainWindow::resizeEvent(event);
    int tableHeight = actionTable->height();
    int rowCount = actionTable->rowCount();
    if (rowCount > 0) {
        int suggestedRowHeight = qMin(40, qMax(30, tableHeight / qMax(1, rowCount) - 2));
        actionTable->verticalHeader()->setDefaultSectionSize(suggestedRowHeight);
    }
}

bool MainWindow::nativeEvent(const QByteArray &eventType, void *message, long *result)
{
    MSG* msg = static_cast<MSG*>(message);
    if (msg->message == WM_HOTKEY) {
        if (msg->wParam == HOTKEY_ID) {
            stopRequested = true;
            if (isSequenceRunning) {
                sequenceTimer->stop();
                countdownTimer->stop();
                isSequenceRunning = false;
                isRepeatingSequence = false;
                isInfiniteSequenceRepeat = false;
                clickButton->setEnabled(true);
                executeSequenceButton->setEnabled(true);
                repeatSequenceButton->setEnabled(true);
                stopSequenceButton->setEnabled(false);
                addActionButton->setEnabled(true);
                removeActionButton->setEnabled(actionTable->currentRow() >= 0);
                clearActionsButton->setEnabled(true);
                importActionsButton->setEnabled(true);
                exportActionsButton->setEnabled(true);
                showStatusMessage("⏹️ 动作序列已通过全局快捷键 Ctrl+C 停止", false, true);
                unregisterGlobalHotkey();
                resetSequenceExecution();
                activateAndShowWindow();
            }
            if (isRepeating) {
                isRepeating = false;
                repeatTimer->stop();
                if (repeatCheckBox->isChecked()) {
                    clickButton->setText("🎯 开始重复点击");
                } else {
                    clickButton->setText("🎯 立即点击");
                }
                executeSequenceButton->setEnabled(true);
                repeatSequenceButton->setEnabled(true);
                importActionsButton->setEnabled(true);
                exportActionsButton->setEnabled(true);
                showStatusMessage("⏹️ 重复点击已通过全局快捷键 Ctrl+C 停止", false, true);
                unregisterGlobalHotkey();
                activateAndShowWindow();
            }
            return true;
        }
    }
    return QMainWindow::nativeEvent(eventType, message, result);
}

bool MainWindow::eventFilter(QObject *obj, QEvent *event)
{
    if (event->type() == QEvent::KeyPress) {
        QKeyEvent *keyEvent = static_cast<QKeyEvent*>(event);
        if (keyEvent->key() == Qt::Key_Space && !keyEvent->isAutoRepeat()) {
            POINT cursorPos;
            GetCursorPos(&cursorPos);
            xSpinBox->setValue(cursorPos.x);
            ySpinBox->setValue(cursorPos.y);
            showStatusMessage(QString("🎯 坐标已拾取: (%1, %2)").arg(cursorPos.x).arg(cursorPos.y), false, true);
            return true;
        }
        if (keyEvent->key() == Qt::Key_Escape && !keyEvent->isAutoRepeat()) {
            if (isSequenceRunning || isRepeating) {
                onStopSequenceClicked();
                return true;
            }
        }
    }
    return QMainWindow::eventFilter(obj, event);
}

void MainWindow::setupShortcuts()
{
    QShortcut *shortcutStartStop = new QShortcut(QKeySequence("Ctrl+S"), this);
    connect(shortcutStartStop, &QShortcut::activated, [this]() {
        if (isSequenceRunning) {
            onStopSequenceClicked();
        } else {
            if (actionList.empty()) {
                onClickButtonClicked();
            } else {
                onExecuteSequenceClicked();
            }
        }
    });

    QShortcut *shortcutRepeat = new QShortcut(QKeySequence("Ctrl+R"), this);
    connect(shortcutRepeat, &QShortcut::activated, this, &MainWindow::onRepeatSequenceClicked);

    QShortcut *shortcutAdd = new QShortcut(QKeySequence("Ctrl+A"), this);
    connect(shortcutAdd, &QShortcut::activated, this, &MainWindow::onAddActionClicked);

    QShortcut *shortcutDelete = new QShortcut(QKeySequence("Ctrl+D"), this);
    connect(shortcutDelete, &QShortcut::activated, this, &MainWindow::onRemoveActionClicked);

    QShortcut *shortcutEsc = new QShortcut(QKeySequence("Esc"), this);
    connect(shortcutEsc, &QShortcut::activated, [this]() {
        if (isSequenceRunning || isRepeating) {
            onStopSequenceClicked();
        }
    });

    // 新增快捷键:Ctrl+I 插入动作,Ctrl+U 上移,Ctrl+J 下移
    QShortcut *shortcutInsert = new QShortcut(QKeySequence("Ctrl+I"), this);
    connect(shortcutInsert, &QShortcut::activated, this, &MainWindow::onInsertAction);

    QShortcut *shortcutMoveUp = new QShortcut(QKeySequence("Ctrl+Up"), this);
    connect(shortcutMoveUp, &QShortcut::activated, this, &MainWindow::onMoveActionUp);

    QShortcut *shortcutMoveDown = new QShortcut(QKeySequence("Ctrl+Down"), this);
    connect(shortcutMoveDown, &QShortcut::activated, this, &MainWindow::onMoveActionDown);
}

void MainWindow::activateAndShowWindow()
{
    if (isMinimized()) {
        showNormal();
    }
    activateWindow();
    raise();
    setWindowState((windowState() & ~Qt::WindowMinimized) | Qt::WindowActive);
}

void MainWindow::showStatusMessage(const QString& message, bool isError, bool isSuccess)
{
    if (isError) {
        statusLabel->setStyleSheet(
            "QLabel {"
            "  background-color: #ffebee;"
            "  color: #c62828;"
            "  border: 2px solid #ffcdd2;"
            "  padding: 10px;"
            "  border-radius: 10px;"
            "  font-weight: 600;"
            "  font-size: 13px;"
            "}"
        );
    } else if (isSuccess) {
        statusLabel->setStyleSheet(
            "QLabel {"
            "  background-color: #e8f5e9;"
            "  color: #2e7d32;"
            "  border: 2px solid #c8e6c9;"
            "  padding: 10px;"
            "  border-radius: 10px;"
            "  font-weight: 600;"
            "  font-size: 13px;"
            "}"
        );
    } else {
        statusLabel->setStyleSheet(
            "QLabel {"
            "  background-color: #e3f2fd;"
            "  color: #1565c0;"
            "  border: 2px solid #bbdefb;"
            "  padding: 10px;"
            "  border-radius: 10px;"
            "  font-weight: 600;"
            "  font-size: 13px;"
            "}"
        );
    }
    statusLabel->setText(message);
}

void MainWindow::updateProgressInfo()
{
    if (isSequenceRunning) {
        if (actionList.empty()) {
            progressLabel->setText("就绪");
            progressBar->setValue(0);
            return;
        }
        int totalActions = actionList.size();
        int currentProgress = 0;
        QString progressText;
        if (isRepeatingSequence) {
            if (isInfiniteSequenceRepeat) {
                progressText = QString("🔁 无限重复执行: 第 %1 次 | 动作: %2/%3")
                              .arg(currentSequenceRepeat)
                              .arg(currentActionIndex + 1)
                              .arg(totalActions);
                currentProgress = 0;
            } else {
                int totalSteps = totalActions * totalSequenceRepeat;
                int completedSteps = (currentSequenceRepeat - 1) * totalActions + currentActionIndex;
                currentProgress = (totalSteps > 0) ? (completedSteps * 100 / totalSteps) : 0;
                progressText = QString("🔁 重复执行: 第 %1/%2 次 | 动作: %3/%4")
                              .arg(currentSequenceRepeat)
                              .arg(totalSequenceRepeat)
                              .arg(currentActionIndex + 1)
                              .arg(totalActions);
            }
        } else {
            currentProgress = (totalActions > 0) ? ((currentActionIndex * 100) / totalActions) : 0;
            progressText = QString("▶️ 执行序列: 动作 %1/%2")
                          .arg(currentActionIndex + 1)
                          .arg(totalActions);
        }
        progressLabel->setText(progressText);
        progressBar->setValue(currentProgress);
    } else if (isRepeating) {
        QString progressText;
        int currentProgress = 0;
        if (infiniteRepeatCheck->isChecked()) {
            int completed = totalRepeatCount - currentRepeatCount;
            progressText = QString("🔁 重复点击: 第 %1 次 (无限循环)").arg(completed + 1);
            currentProgress = 0;
        } else {
            int completed = totalRepeatCount - currentRepeatCount;
            currentProgress = (totalRepeatCount > 0) ? (completed * 100 / totalRepeatCount) : 0;
            progressText = QString("🔁 重复点击: %1/%2 次").arg(completed + 1).arg(totalRepeatCount);
        }
        progressLabel->setText(progressText);
        progressBar->setValue(currentProgress);
    } else {
        progressLabel->setText("就绪");
        progressBar->setValue(0);
    }
}

void MainWindow::startCountdown(int seconds)
{
    countdownSeconds = seconds;
    QString modeText;
    if (isRepeatingSequence) {
        if (isInfiniteSequenceRepeat) {
            modeText = "无限重复执行序列";
        } else {
            modeText = QString("重复执行序列 (%1次)").arg(totalSequenceRepeat);
        }
    } else {
        modeText = "执行序列";
    }
    if (countdownSeconds > 0) {
        showStatusMessage(QString("⏱️ 倒计时: %1 秒后开始%2...").arg(countdownSeconds).arg(modeText));
        countdownTimer->start();
    } else {
        onCountdownTimeout();
    }
}

int MainWindow::delayToMilliseconds(double seconds)
{
    return static_cast<int>(seconds * 1000);
}

void MainWindow::startSequenceExecution(bool repeatMode)
{
    if (actionList.empty()) {
        showStatusMessage("⚠️ 动作列表为空,请先添加动作", true);
        QMessageBox::warning(this, "警告", "动作列表为空,请先添加动作!");
        return;
    }
    if (isRepeating) {
        isRepeating = false;
        repeatTimer->stop();
        clickButton->setText("🎯 开始重复点击");
        showStatusMessage("⏹️ 重复点击已停止", false, true);
        unregisterGlobalHotkey();
    }
    resetSequenceExecution();
    isRepeatingSequence = repeatMode;
    isInfiniteSequenceRepeat = infiniteSequenceRepeatCheck->isChecked();
    if (isRepeatingSequence) {
        if (isInfiniteSequenceRepeat) {
            totalSequenceRepeat = 0;
        } else {
            totalSequenceRepeat = sequenceRepeatSpinBox->value();
        }
        currentSequenceRepeat = 1;
    } else {
        totalSequenceRepeat = 1;
        currentSequenceRepeat = 1;
        isInfiniteSequenceRepeat = false;
    }
    clickButton->setEnabled(false);
    executeSequenceButton->setEnabled(false);
    repeatSequenceButton->setEnabled(false);
    stopSequenceButton->setEnabled(true);
    addActionButton->setEnabled(false);
    removeActionButton->setEnabled(false);
    clearActionsButton->setEnabled(false);
    sequenceRepeatSpinBox->setEnabled(false);
    infiniteSequenceRepeatCheck->setEnabled(false);
    importActionsButton->setEnabled(false);
    exportActionsButton->setEnabled(false);
    registerGlobalHotkey();
    int countdownTime = countdownSpinBox->value();
    startCountdown(countdownTime);
    isSequenceRunning = true;
    stopRequested = false;
    updateProgressInfo();
}

void MainWindow::clickAt(int x, int y, bool leftButton, bool doubleClick)
{
    POINT originalPos;
    GetCursorPos(&originalPos);
    moveMouseTo(x, y);
    if (doubleClick) {
        sendMouseClick(x, y, leftButton, true);
        sendMouseClick(x, y, leftButton, false);
        Sleep(50);
        sendMouseClick(x, y, leftButton, true);
        sendMouseClick(x, y, leftButton, false);
    } else {
        sendMouseClick(x, y, leftButton, true);
        sendMouseClick(x, y, leftButton, false);
    }
}

void MainWindow::clickAction(const ClickAction& action)
{
    QString modeText = isRepeatingSequence ? "重复执行中" : "执行中";
    QString buttonText = action.leftButton ? "左键" : "右键";
    QString clickType = action.doubleClick ? "双击" : "单击";
    showStatusMessage(QString("%1 - 动作 %2: (%3, %4) | %5 %6 | 延迟: %7秒")
                     .arg(modeText)
                     .arg(action.id)
                     .arg(action.x)
                     .arg(action.y)
                     .arg(buttonText)
                     .arg(clickType)
                     .arg(QString::number(action.delay, 'f', 3)));
    clickAt(action.x, action.y, action.leftButton, action.doubleClick);
    updateProgressInfo();
}

void MainWindow::moveMouseTo(int x, int y)
{
    INPUT input = {0};
    input.type = INPUT_MOUSE;
    input.mi.dx = x * (65535.0f / GetSystemMetrics(SM_CXSCREEN));
    input.mi.dy = y * (65535.0f / GetSystemMetrics(SM_CYSCREEN));
    input.mi.dwFlags = MOUSEEVENTF_MOVE | MOUSEEVENTF_ABSOLUTE;
    SendInput(1, &input, sizeof(INPUT));
}

void MainWindow::sendMouseClick(int x, int y, bool leftButton, bool down)
{
    INPUT input = {0};
    input.type = INPUT_MOUSE;
    if (leftButton) {
        if (down) {
            input.mi.dwFlags = MOUSEEVENTF_LEFTDOWN;
        } else {
            input.mi.dwFlags = MOUSEEVENTF_LEFTUP;
        }
    } else {
        if (down) {
            input.mi.dwFlags = MOUSEEVENTF_RIGHTDOWN;
        } else {
            input.mi.dwFlags = MOUSEEVENTF_RIGHTUP;
        }
    }
    SendInput(1, &input, sizeof(INPUT));
}

void MainWindow::addActionToList(const ClickAction& action)
{
    actionList.push_back(action);
    updateActionTable();
}

void MainWindow::updateActionTable()
{
    actionTable->blockSignals(true);
    actionTable->setRowCount(actionList.size());
    for (size_t i = 0; i < actionList.size(); ++i) {
        const ClickAction& action = actionList[i];
        QTableWidgetItem *idItem = new QTableWidgetItem(QString::number(action.id));
        idItem->setFlags(idItem->flags() & ~Qt::ItemIsEditable);
        idItem->setTextAlignment(Qt::AlignCenter);
        idItem->setData(Qt::UserRole, QVariant(action.id));
        actionTable->setItem(i, 0, idItem);
        QTableWidgetItem *xItem = new QTableWidgetItem(QString::number(action.x));
        xItem->setTextAlignment(Qt::AlignCenter);
        actionTable->setItem(i, 1, xItem);
        QTableWidgetItem *yItem = new QTableWidgetItem(QString::number(action.y));
        yItem->setTextAlignment(Qt::AlignCenter);
        actionTable->setItem(i, 2, yItem);
        QTableWidgetItem *delayItem = new QTableWidgetItem(QString::number(action.delay, 'f', 3));
        delayItem->setTextAlignment(Qt::AlignCenter);
        actionTable->setItem(i, 3, delayItem);
        QTableWidgetItem *buttonItem = new QTableWidgetItem(action.leftButton ? "左键" : "右键");
        buttonItem->setTextAlignment(Qt::AlignCenter);
        actionTable->setItem(i, 4, buttonItem);
        QTableWidgetItem *doubleItem = new QTableWidgetItem(action.doubleClick ? "是" : "否");
        doubleItem->setTextAlignment(Qt::AlignCenter);
        actionTable->setItem(i, 5, doubleItem);
    }
    actionTable->blockSignals(false);
    resizeEvent(nullptr);
}

// 保存为JSON格式 - 优化版本,更易于手动编辑
bool MainWindow::saveActionsToJson(const QString& filePath)
{
    QFile file(filePath);
    if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
        return false;
    }

    QJsonArray actionsArray;
    for (const auto& action : actionList) {
        QJsonObject actionObj;
        actionObj["id"] = action.id;
        actionObj["x"] = action.x;
        actionObj["y"] = action.y;
        actionObj["delay"] = QString::number(action.delay, 'f', 3);  // 保存为字符串,便于编辑
        actionObj["button"] = action.leftButton ? "left" : "right";  // 使用英文,便于理解
        actionObj["double_click"] = action.doubleClick;  // 布尔值,便于编辑

        actionsArray.append(actionObj);
    }

    QJsonObject rootObj;
    rootObj["app_name"] = "智能鼠标点击器";
    rootObj["version"] = "2.0";
    rootObj["description"] = "鼠标动作序列配置文件";
    rootObj["total_actions"] = static_cast<int>(actionList.size());
    rootObj["export_time"] = QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss");
    rootObj["actions"] = actionsArray;

    QJsonDocument doc(rootObj);

    // 使用缩进格式化,便于阅读和编辑
    QByteArray jsonData = doc.toJson(QJsonDocument::Indented);
    file.write(jsonData);
    file.close();

    return true;
}

// 从JSON格式加载
bool MainWindow::loadActionsFromJson(const QString& filePath)
{
    QFile file(filePath);
    if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
        return false;
    }

    QByteArray data = file.readAll();
    file.close();

    QJsonDocument doc = QJsonDocument::fromJson(data);
    if (doc.isNull() || !doc.isObject()) {
        return false;
    }

    QJsonObject rootObj = doc.object();

    // 检查必需字段
    if (!rootObj.contains("actions") || !rootObj["actions"].isArray()) {
        return false;
    }

    QJsonArray actionsArray = rootObj["actions"].toArray();
    std::vector<ClickAction> newActionList;
    int maxId = 0;

    for (int i = 0; i < actionsArray.size(); ++i) {
        QJsonObject actionObj = actionsArray[i].toObject();

        // 检查必需字段
        if (!actionObj.contains("id") || !actionObj.contains("x") ||
            !actionObj.contains("y") || !actionObj.contains("delay") ||
            !actionObj.contains("button") || !actionObj.contains("double_click")) {
            continue;  // 跳过无效的动作
        }

        ClickAction action;
        action.id = actionObj["id"].toInt();

        // 更新最大ID
        if (action.id > maxId) {
            maxId = action.id;
        }

        action.x = actionObj["x"].toInt();
        action.y = actionObj["y"].toInt();

        // 处理延迟时间(支持字符串和数字)
        QJsonValue delayValue = actionObj["delay"];
        if (delayValue.isString()) {
            action.delay = delayValue.toString().toDouble();
        } else {
            action.delay = delayValue.toDouble();
        }

        // 处理按钮类型
        QString buttonType = actionObj["button"].toString().toLower();
        action.leftButton = (buttonType == "left" || buttonType == "左键" || buttonType == "leftbutton");

        // 处理双击
        action.doubleClick = actionObj["double_click"].toBool();

        // 生成描述
        action.description = QString("动作 %1: (%2, %3) 延迟: %4秒 %5 %6")
                           .arg(action.id)
                           .arg(action.x)
                           .arg(action.y)
                           .arg(QString::number(action.delay, 'f', 3))
                           .arg(action.leftButton ? "左键" : "右键")
                           .arg(action.doubleClick ? "双击" : "单击");

        newActionList.push_back(action);
    }

    if (!newActionList.empty()) {
        actionList = newActionList;
        nextActionId = maxId + 1;  // 设置下一个ID
        updateActionTable();
        return true;
    }

    return false;
}

void MainWindow::onRepeatStateChanged(int state)
{
    bool enabled = (state == Qt::Checked);
    repeatCountSpinBox->setEnabled(enabled);
    infiniteRepeatCheck->setEnabled(enabled);
    if (enabled) {
        clickButton->setText("🎯 开始重复点击");
        showStatusMessage("🔁 已启用重复点击模式");
    } else {
        clickButton->setText("🎯 立即点击");
        showStatusMessage("✅ 已禁用重复点击模式");
        if (isRepeating) {
            isRepeating = false;
            repeatTimer->stop();
            clickButton->setText("🎯 立即点击");
            executeSequenceButton->setEnabled(true);
            repeatSequenceButton->setEnabled(true);
            importActionsButton->setEnabled(true);
            exportActionsButton->setEnabled(true);
            unregisterGlobalHotkey();
            activateAndShowWindow();
        }
    }
}

void MainWindow::onRepeatCountChanged(int value)
{
    infiniteRepeatCheck->setChecked(false);
}

void MainWindow::onInfiniteSequenceRepeatChanged(int state)
{
    bool enabled = (state == Qt::Checked);
    sequenceRepeatSpinBox->setEnabled(!enabled);
}

void MainWindow::onPickCoordClicked()
{
    POINT cursorPos;
    GetCursorPos(&cursorPos);
    xSpinBox->setValue(cursorPos.x);
    ySpinBox->setValue(cursorPos.y);
    showStatusMessage(QString("🎯 坐标已拾取: (%1, %2)").arg(cursorPos.x).arg(cursorPos.y), false, true);
}

void MainWindow::onClickButtonClicked()
{
    int x = xSpinBox->value();
    int y = ySpinBox->value();
    bool leftButton = leftButtonCheck->isChecked();
    bool doubleClick = doubleClickCheck->isChecked();
    double delaySeconds = delaySpinBox->value();
    if (repeatCheckBox->isChecked()) {
        if (isRepeating) {
            isRepeating = false;
            repeatTimer->stop();
            clickButton->setText("🎯 开始重复点击");
            showStatusMessage("⏹️ 重复点击已停止", false, true);
            executeSequenceButton->setEnabled(true);
            repeatSequenceButton->setEnabled(true);
            importActionsButton->setEnabled(true);
            exportActionsButton->setEnabled(true);
            progressBar->setValue(0);
            progressLabel->setText("就绪");
            unregisterGlobalHotkey();
            activateAndShowWindow();
        } else {
            registerGlobalHotkey();
            int countdownTime = countdownSpinBox->value();
            if (countdownTime > 0) {
                isRepeating = true;
                stopRequested = false;
                countdownSeconds = countdownTime;
                if (infiniteRepeatCheck->isChecked()) {
                    totalRepeatCount = 0;
                    currentRepeatCount = 0;
                } else {
                    totalRepeatCount = repeatCountSpinBox->value();
                    currentRepeatCount = repeatCountSpinBox->value();
                }
                showStatusMessage(QString("⏱️ 倒计时: %1 秒后开始重复点击...").arg(countdownSeconds));
                countdownTimer->start();
                clickButton->setText("⏹️ 停止重复点击");
                executeSequenceButton->setEnabled(false);
                repeatSequenceButton->setEnabled(false);
                importActionsButton->setEnabled(false);
                exportActionsButton->setEnabled(false);
            } else {
                isRepeating = true;
                stopRequested = false;
                if (infiniteRepeatCheck->isChecked()) {
                    totalRepeatCount = 0;
                    currentRepeatCount = 0;
                } else {
                    totalRepeatCount = repeatCountSpinBox->value();
                    currentRepeatCount = repeatCountSpinBox->value();
                }
                int delayMs = delayToMilliseconds(delaySeconds);
                repeatTimer->start(delayMs);
                clickButton->setText("⏹️ 停止重复点击");
                if (infiniteRepeatCheck->isChecked()) {
                    showStatusMessage(QString("🔁 开始无限重复点击 (间隔: %1秒)").arg(QString::number(delaySeconds, 'f', 3)));
                } else {
                    showStatusMessage(QString("🔁 开始重复点击 %1 次 (间隔: %2秒)").arg(totalRepeatCount).arg(QString::number(delaySeconds, 'f', 3)));
                }
                clickAt(x, y, leftButton, doubleClick);
                if (totalRepeatCount > 0) {
                    currentRepeatCount--;
                }
                if (totalRepeatCount > 0 && currentRepeatCount <= 0) {
                    isRepeating = false;
                    repeatTimer->stop();
                    clickButton->setText("🎯 开始重复点击");
                    showStatusMessage(QString("✅ 重复点击完成,共 %1 次").arg(totalRepeatCount), false, true);
                    executeSequenceButton->setEnabled(true);
                    repeatSequenceButton->setEnabled(true);
                    importActionsButton->setEnabled(true);
                    exportActionsButton->setEnabled(true);
                    progressBar->setValue(100);
                    progressLabel->setText("完成");
                    unregisterGlobalHotkey();
                    activateAndShowWindow();
                } else {
                    executeSequenceButton->setEnabled(false);
                    repeatSequenceButton->setEnabled(false);
                    importActionsButton->setEnabled(false);
                    exportActionsButton->setEnabled(false);
                }
                updateProgressInfo();
            }
        }
    } else {
        showStatusMessage(QString("🎯 正在点击 (%1, %2)").arg(x).arg(y));
        clickAt(x, y, leftButton, doubleClick);
        showStatusMessage(QString("✅ 点击完成 (%1, %2)").arg(x).arg(y), false, true);
        QTimer::singleShot(1000, this, [this]() {
            showStatusMessage("✅ 就绪", false, true);
        });
    }
}

void MainWindow::onRepeatTimeout()
{
    if (stopRequested) {
        isRepeating = false;
        repeatTimer->stop();
        clickButton->setText("🎯 开始重复点击");
        showStatusMessage("⏹️ 重复点击已停止", false, true);
        executeSequenceButton->setEnabled(true);
        repeatSequenceButton->setEnabled(true);
        importActionsButton->setEnabled(true);
        exportActionsButton->setEnabled(true);
        progressBar->setValue(0);
        progressLabel->setText("就绪");
        unregisterGlobalHotkey();
        activateAndShowWindow();
        return;
    }
    int x = xSpinBox->value();
    int y = ySpinBox->value();
    bool leftButton = leftButtonCheck->isChecked();
    bool doubleClick = doubleClickCheck->isChecked();
    clickAt(x, y, leftButton, doubleClick);
    if (totalRepeatCount > 0) {
        currentRepeatCount--;
        if (currentRepeatCount <= 0) {
            isRepeating = false;
            repeatTimer->stop();
            clickButton->setText("🎯 开始重复点击");
            showStatusMessage(QString("✅ 重复点击完成,共 %1 次").arg(totalRepeatCount), false, true);
            executeSequenceButton->setEnabled(true);
            repeatSequenceButton->setEnabled(true);
            importActionsButton->setEnabled(true);
            exportActionsButton->setEnabled(true);
            progressBar->setValue(100);
            progressLabel->setText("完成");
            unregisterGlobalHotkey();
            activateAndShowWindow();
        }
    }
    updateProgressInfo();
}

void MainWindow::onCountdownTimeout()
{
    countdownSeconds--;
    if (countdownSeconds > 0) {
        if (isRepeating) {
            showStatusMessage(QString("⏱️ 倒计时: %1 秒后开始重复点击...").arg(countdownSeconds));
        } else {
            QString modeText;
            if (isRepeatingSequence) {
                if (isInfiniteSequenceRepeat) {
                    modeText = "无限重复执行序列";
                } else {
                    modeText = QString("重复执行序列 (%1次)").arg(totalSequenceRepeat);
                }
            } else {
                modeText = "执行序列";
            }
            showStatusMessage(QString("⏱️ 倒计时: %1 秒后开始%2...").arg(countdownSeconds).arg(modeText));
        }
    } else {
        countdownTimer->stop();
        if (isRepeating) {
            int x = xSpinBox->value();
            int y = ySpinBox->value();
            bool leftButton = leftButtonCheck->isChecked();
            bool doubleClick = doubleClickCheck->isChecked();
            double delaySeconds = delaySpinBox->value();
            int delayMs = delayToMilliseconds(delaySeconds);
            repeatTimer->start(delayMs);
            if (infiniteRepeatCheck->isChecked()) {
                showStatusMessage(QString("🔁 开始无限重复点击 (间隔: %1秒)").arg(QString::number(delaySeconds, 'f', 3)));
            } else {
                showStatusMessage(QString("🔁 开始重复点击 %1 次 (间隔: %2秒)").arg(totalRepeatCount).arg(QString::number(delaySeconds, 'f', 3)));
            }
            clickAt(x, y, leftButton, doubleClick);
            if (totalRepeatCount > 0) {
                currentRepeatCount--;
            }
            if (totalRepeatCount > 0 && currentRepeatCount <= 0) {
                isRepeating = false;
                repeatTimer->stop();
                clickButton->setText("🎯 开始重复点击");
                showStatusMessage(QString("✅ 重复点击完成,共 %1 次").arg(totalRepeatCount), false, true);
                executeSequenceButton->setEnabled(true);
                repeatSequenceButton->setEnabled(true);
                importActionsButton->setEnabled(true);
                exportActionsButton->setEnabled(true);
                progressBar->setValue(100);
                progressLabel->setText("完成");
                unregisterGlobalHotkey();
                activateAndShowWindow();
            }
            updateProgressInfo();
        } else {
            QString modeText;
            if (isRepeatingSequence) {
                if (isInfiniteSequenceRepeat) {
                    modeText = "开始无限重复执行动作序列";
                } else {
                    modeText = QString("开始重复执行动作序列 (%1次)").arg(totalSequenceRepeat);
                }
            } else {
                modeText = "开始执行动作序列";
            }
            showStatusMessage(QString("▶️ %1").arg(modeText));
            currentActionIndex = 0;
            onSequenceTimeout();
        }
    }
}

void MainWindow::onAddActionClicked()
{
    int x = xSpinBox->value();
    int y = ySpinBox->value();
    double delaySeconds = delaySpinBox->value();
    bool leftButton = leftButtonCheck->isChecked();
    bool doubleClick = doubleClickCheck->isChecked();
    if (delaySeconds < 0.001 || delaySeconds > 10800.0) {
        showStatusMessage("⚠️ 延迟时间必须在0.001秒到3小时之间!", true);
        QMessageBox::warning(this, "警告", "延迟时间必须在0.001秒到3小时之间!");
        return;
    }
    QString description = QString("动作 %1: (%2, %3) 延迟: %4秒")
                         .arg(nextActionId).arg(x).arg(y).arg(QString::number(delaySeconds, 'f', 3));
    ClickAction newAction(nextActionId++, x, y, delaySeconds, leftButton, doubleClick, description);
    addActionToList(newAction);
    showStatusMessage(QString("✅ 已添加动作 %1").arg(newAction.id), false, true);
}

void MainWindow::onRemoveActionClicked()
{
    int currentRow = actionTable->currentRow();
    if (currentRow >= 0 && currentRow < (int)actionList.size()) {
        int id = actionList[currentRow].id;
        actionList.erase(actionList.begin() + currentRow);
        updateActionTable();
        showStatusMessage(QString("✅ 已移除动作 %1").arg(id), false, true);
    }
}

void MainWindow::onClearActionsClicked()
{
    if (!actionList.empty()) {
        QMessageBox::StandardButton reply;
        reply = QMessageBox::question(this, "确认清空",
                                     "确定要清空所有动作吗?此操作不可撤销!",
                                     QMessageBox::Yes | QMessageBox::No);
        if (reply == QMessageBox::Yes) {
            actionList.clear();
            updateActionTable();
            nextActionId = 1;
            showStatusMessage("✅ 所有动作已清空", false, true);
        }
    }
}

// 导入动作序列
void MainWindow::onImportActionsClicked()
{
    QString filePath = QFileDialog::getOpenFileName(this, "导入动作序列",
                                                   QDir::homePath(),
                                                   "JSON文件 (*.json);;所有文件 (*.*)");
    if (filePath.isEmpty()) {
        return;
    }

    if (loadActionsFromJson(filePath)) {
        showStatusMessage(QString("✅ 已从文件导入 %1 个动作").arg(actionList.size()), false, true);
    } else {
        showStatusMessage("❌ 导入失败:文件格式错误或无法读取", true);
        QMessageBox::critical(this, "导入失败",
                            "无法读取文件,请确保:\n"
                            "1. 文件是有效的JSON格式\n"
                            "2. 文件包含正确的动作数据\n"
                            "3. 每个动作都包含必需的字段:id, x, y, delay, button, double_click");
    }
}

// 导出动作序列
void MainWindow::onExportActionsClicked()
{
    if (actionList.empty()) {
        showStatusMessage("⚠️ 没有动作可以导出", true);
        QMessageBox::warning(this, "警告", "动作列表为空,无法导出!");
        return;
    }

    QString defaultFileName = QString("mouse_actions_%1.json")
                             .arg(QDateTime::currentDateTime().toString("yyyyMMdd_hhmmss"));

    QString filePath = QFileDialog::getSaveFileName(this, "导出动作序列",
                                                   QDir::homePath() + "/" + defaultFileName,
                                                   "JSON文件 (*.json)");
    if (filePath.isEmpty()) {
        return;
    }

    // 确保文件扩展名
    if (!filePath.endsWith(".json", Qt::CaseInsensitive)) {
        filePath += ".json";
    }

    if (saveActionsToJson(filePath)) {
        showStatusMessage(QString("✅ 已导出 %1 个动作到文件").arg(actionList.size()), false, true);
        QMessageBox::information(this, "导出成功",
                                QString("动作序列已成功导出到:\n%1\n\n"
                                       "共 %2 个动作。\n\n"
                                       "文件采用易读的JSON格式,您可以使用文本编辑器直接修改。")
                                .arg(filePath).arg(actionList.size()));
    } else {
        showStatusMessage("❌ 导出失败:无法写入文件", true);
        QMessageBox::critical(this, "导出失败", "无法写入文件,请检查文件权限或磁盘空间。");
    }
}

void MainWindow::onExecuteSequenceClicked()
{
    startSequenceExecution(false);
}

void MainWindow::onRepeatSequenceClicked()
{
    startSequenceExecution(true);
}

void MainWindow::onStopSequenceClicked()
{
    stopRequested = true;
    isSequenceRunning = false;
    isRepeatingSequence = false;
    isInfiniteSequenceRepeat = false;
    sequenceTimer->stop();
    countdownTimer->stop();
    if (isRepeating) {
        isRepeating = false;
        repeatTimer->stop();
        clickButton->setText("🎯 开始重复点击");
    }
    clickButton->setEnabled(true);
    executeSequenceButton->setEnabled(true);
    repeatSequenceButton->setEnabled(true);
    stopSequenceButton->setEnabled(false);
    addActionButton->setEnabled(true);
    removeActionButton->setEnabled(actionTable->currentRow() >= 0);
    clearActionsButton->setEnabled(true);
    sequenceRepeatSpinBox->setEnabled(true);
    infiniteSequenceRepeatCheck->setEnabled(true);
    importActionsButton->setEnabled(true);
    exportActionsButton->setEnabled(true);
    unregisterGlobalHotkey();
    resetSequenceExecution();
    showStatusMessage("⏹️ 动作序列已停止", false, true);
    progressBar->setValue(0);
    progressLabel->setText("已停止");
    activateAndShowWindow();
    for (int i = 0; i < actionTable->rowCount(); ++i) {
        for (int j = 0; j < actionTable->columnCount(); ++j) {
            QTableWidgetItem *item = actionTable->item(i, j);
            if (item) {
                item->setBackground(QBrush());
                item->setForeground(QBrush());
                item->setFont(QFont());
            }
        }
    }
}

void MainWindow::onSequenceTimeout()
{
    if (stopRequested) {
        onStopSequenceClicked();
        return;
    }
    if (currentActionIndex >= (int)actionList.size()) {
        if (isRepeatingSequence) {
            if (!isInfiniteSequenceRepeat && currentSequenceRepeat >= totalSequenceRepeat) {
                onStopSequenceClicked();
                showStatusMessage(QString("✅ 序列重复执行完成,共 %1 次").arg(totalSequenceRepeat), false, true);
                progressBar->setValue(100);
                progressLabel->setText("完成");
                activateAndShowWindow();
                return;
            } else {
                currentSequenceRepeat++;
                currentActionIndex = 0;
                if (isInfiniteSequenceRepeat) {
                    showStatusMessage(QString("🔁 开始第 %1 次无限重复执行").arg(currentSequenceRepeat));
                } else {
                    showStatusMessage(QString("🔁 开始第 %1/%2 次重复执行").arg(currentSequenceRepeat).arg(totalSequenceRepeat));
                }
            }
        } else {
            onStopSequenceClicked();
            showStatusMessage("✅ 动作序列执行完成", false, true);
            progressBar->setValue(100);
            progressLabel->setText("完成");
            activateAndShowWindow();
            return;
        }
    }
    const ClickAction& action = actionList[currentActionIndex];
    clickAction(action);
    if (currentActionIndex < actionTable->rowCount()) {
        for (int i = 0; i < actionTable->rowCount(); ++i) {
            for (int j = 0; j < actionTable->columnCount(); ++j) {
                QTableWidgetItem *item = actionTable->item(i, j);
                if (item) {
                    if (i == currentActionIndex - 1) {
                        item->setBackground(QBrush());
                        item->setForeground(QBrush());
                        item->setFont(QFont());
                    }
                    if (i == currentActionIndex) {
                        item->setBackground(QColor(255, 235, 180));
                        item->setForeground(QColor(102, 60, 0));
                        item->setFont(QFont("", -1, QFont::Bold));
                    }
                }
            }
        }
        actionTable->scrollToItem(actionTable->item(currentActionIndex, 0), QAbstractItemView::EnsureVisible);
    }
    currentActionIndex++;
    if (currentActionIndex < (int)actionList.size()) {
        const ClickAction& nextAction = actionList[currentActionIndex];
        int delayMs = delayToMilliseconds(nextAction.delay);
        sequenceTimer->start(delayMs);
    } else {
        QTimer::singleShot(100, this, &MainWindow::onSequenceTimeout);
    }
}

void MainWindow::onActionTableItemChanged(QTableWidgetItem *item)
{
    int row = item->row();
    int column = item->column();
    if (row >= 0 && row < (int)actionList.size()) {
        ClickAction& action = actionList[row];
        switch (column) {
        case 1:
            action.x = item->text().toInt();
            break;
        case 2:
            action.y = item->text().toInt();
            break;
        case 3:
            action.delay = item->text().toDouble();
            if (action.delay < 0.001 || action.delay > 10800.0) {
                action.delay = qBound(0.001, action.delay, 10800.0);
                item->setText(QString::number(action.delay, 'f', 3));
                showStatusMessage("⚠️ 延迟时间已自动调整到有效范围", true);
            }
            break;
        case 4:
            action.leftButton = (item->text() == "左键");
            break;
        case 5:
            action.doubleClick = (item->text() == "是");
            break;
        }
        action.description = QString("动作 %1: (%2, %3) 延迟: %4秒")
                           .arg(action.id).arg(action.x).arg(action.y).arg(QString::number(action.delay, 'f', 3));
        showStatusMessage(QString("📝 已更新动作 %1").arg(action.id), false, true);
    }
}

void MainWindow::onActionTableSelectionChanged()
{
    bool hasSelection = actionTable->currentRow() >= 0;
    removeActionButton->setEnabled(hasSelection);
}

main.cpp

#include "mainwindow.h"
#include <QApplication>
#include <QTextCodec>

int main(int argc, char *argv[])
{
    // 设置应用程序编码
    QTextCodec::setCodecForLocale(QTextCodec::codecForName("UTF-8"));

    QApplication app(argc, argv);

    // 设置应用程序信息
    app.setApplicationName("动作序列鼠标点击器");
    app.setApplicationVersion("1.0");

    // 创建并显示主窗口
    MainWindow window;
    window.show();

    return app.exec();
}

智能鼠标点击器使用说明书

一、软件简介

智能鼠标点击器是一款功能强大的Windows自动化工具,支持:

  • 基础点击功能:单次点击、重复点击(支持无限循环)

  • 动作序列功能:创建、编辑、执行复杂的鼠标点击序列

  • 全局热键控制:即使软件不在前台也能控制

  • 坐标拾取功能:快速获取鼠标当前位置

  • 进度可视化:实时显示执行进度

  • 数据保存:自动保存动作列表


二、系统要求

  • 操作系统:Windows 7/8/10/11(64位/32位)

  • 运行环境:需要安装Qt运行库或编译为独立可执行文件

  • 权限要求:需要管理员权限以正常使用全局热键功能


三、界面布局

3.1 基础点击控制区域(🎯 绿色区域)

  • X/Y坐标输入框:设置点击位置的屏幕坐标

  • 延迟时间:设置点击间隔(0.01秒~3小时)

  • 拾取坐标按钮:快速获取当前鼠标位置

  • 点击选项:选择左键/右键、单击/双击

  • 重复点击设置:启用重复点击、设置次数或无限循环

  • 立即点击按钮:执行点击操作

3.2 动作序列管理区域(📋 蓝色区域)

  • 动作表格:显示所有已添加的动作

    • ID:动作编号(自动生成)

    • X/Y坐标:点击位置

    • 延迟:执行后等待时间

    • 按键:左键或右键

    • 双击:是否执行双击操作

  • 序列重复设置:设置序列重复次数或无限循环

  • 控制按钮:添加、移除、清空、执行、停止动作序列

3.3 状态显示区域

  • 进度条:显示当前执行进度

  • 进度文本:显示详细执行信息

  • 状态栏:显示软件当前状态和提示信息

  • 光标位置:实时显示鼠标当前位置


四、基础功能介绍 

4.1 单次点击

  1. 设置坐标

    • 手动输入X、Y坐标值

    • 或点击"拾取坐标"按钮,移动鼠标到目标位置后按空格键

  2. 配置点击参数

    • 选择点击按钮(左键/右键)

    • 选择点击类型(单击/双击)

    • 设置延迟时间(如果需要)

  3. 执行点击

    • 确保"启用重复点击"未勾选

    • 点击"立即点击"按钮

    • 或按快捷键Ctrl+S

4.2 重复点击

  1. 启用重复功能

    • 勾选"🔁 启用重复点击"

    • 设置重复次数(或勾选"无限循环")

  2. 开始重复点击

    • 点击"立即点击"按钮(按钮文本变为"⏹️ 停止重复点击")

    • 软件会按照设定的间隔重复执行点击

  3. 停止重复点击

    • 再次点击"立即点击"按钮

    • 或按Ctrl+C(全局热键)

    • 或按Esc

4.3 坐标拾取技巧

  • 快速拾取:随时按空格键可拾取当前鼠标位置

  • 精确拾取:点击"拾取坐标"按钮进入拾取模式,移动鼠标到目标位置后按空格键


五、动作序列功能 

5.1 创建动作序列

方法一:逐个添加动作
  1. 在基础区域设置好点击参数

  2. 点击"➕ 添加动作"按钮(快捷键:Ctrl+A

  3. 重复以上步骤添加多个动作

方法二:编辑已有动作
  • 修改参数:双击表格中的单元格直接编辑

  • 调整顺序:目前不支持直接拖拽,需按执行顺序添加

5.2 管理动作序列

  • 删除动作:选中表格中的行,点击"➖ 移除动作"(快捷键:Ctrl+D

  • 清空所有:点击"🗑️ 清空动作"按钮(需确认)

  • 自动保存:退出软件时自动保存,下次启动自动加载

5.3 执行动作序列

单次执行序列
  1. 添加至少一个动作到序列

  2. 点击"▶️ 执行序列"按钮(快捷键:Ctrl+S

  3. 等待3秒倒计时后开始执行

  4. 执行过程中当前动作会高亮显示

重复执行序列
  1. 设置重复次数(或勾选"无限循环")

  2. 点击"🔁 重复执行序列"按钮(快捷键:Ctrl+R

  3. 序列会按照设定的次数重复执行

停止执行
  • 点击"⏹️ 停止序列"按钮

  • 或按Ctrl+C(全局热键,即使软件最小化也有效)

  • 或按Esc

5.4 序列执行说明

  • 倒计时机制:开始执行前有3秒倒计时,便于准备

  • 进度显示:执行过程中显示详细进度信息

  • 表格高亮:当前执行的动作行会高亮显示

  • 自动滚动:表格会自动滚动到当前执行的动作


六、快捷键说明 

全局快捷键(任何情况下都有效)

快捷键功能说明
空格键拾取坐标拾取当前鼠标位置到X/Y输入框
Ctrl+C停止所有操作全局热键,即使软件不在前台也有效
Esc停止当前操作停止正在执行的点击或序列

窗口内快捷键

快捷键功能说明
Ctrl+S开始/停止根据情况开始或停止操作
Ctrl+R重复执行序列开始重复执行动作序列
Ctrl+A添加动作将当前设置添加到动作序列
Ctrl+D删除动作删除选中的动作

七、高级功能 

7.1 无限循环模式

基础点击无限循环
  1. 勾选"启用重复点击"

  2. 勾选"无限循环"

  3. 点击"立即点击"开始

  4. Ctrl+CEsc停止

动作序列无限循环
  1. 添加动作到序列

  2. 勾选"无限循环"

  3. 点击"重复执行序列"

  4. 序列将无限重复执行直到手动停止

7.2 延迟时间设置

  • 范围:0.01秒 ~ 3小时(10800秒)

  • 精度:支持两位小数(0.01秒)

  • 用途

    • 重复点击间隔

    • 动作序列中动作之间的等待时间

7.3 坐标范围

  • X坐标:0 ~ (屏幕宽度-1)

  • Y坐标:0 ~ (屏幕高度-1)

  • 自动限制:输入超出范围的值会自动调整

7.4 数据持久化

  • 自动保存:退出程序时自动保存动作序列到actions.dat文件

  • 自动加载:启动程序时自动加载上次保存的动作序列

  • 文件位置:与程序同目录下的actions.dat文件


八、常见问题 

Q1: 点击没有反应怎么办?

可能原因及解决方法:

  1. 坐标超出屏幕范围:检查坐标值是否在屏幕范围内

  2. 目标窗口被遮挡:确保目标窗口在最前面

  3. 权限问题:尝试以管理员身份运行程序

  4. 防病毒软件拦截:检查防病毒软件是否阻止了程序

Q2: 全局热键Ctrl+C无效?

解决方法:

  1. 确保程序正在运行

  2. 检查是否有其他程序占用了Ctrl+C热键

  3. 以管理员身份重新启动程序

Q3: 动作序列执行顺序错乱?

可能原因:

  1. 延迟时间设置过短,系统来不及响应

  2. 动作之间没有足够的等待时间
    建议: 适当增加延迟时间,特别是连续点击相同位置时

Q4: 如何精确设置坐标?

建议方法:

  1. 使用"拾取坐标"功能配合空格键

  2. 对于需要精确计算的位置,可以使用屏幕标尺工具

  3. 先设置大致位置,再通过微调找到最佳位置

Q5: 程序崩溃或异常退出?

处理措施:

  1. 重新启动程序

  2. 检查actions.dat文件是否损坏(可删除该文件让程序重新创建)

  3. 查看Windows事件查看器中的错误日志


九、注意事项

9.1 使用建议

  1. 测试再使用:正式使用前先用少量次数测试

  2. 合理设置延迟:避免设置过短的延迟导致系统响应不过来

  3. 注意目标窗口:确保执行时目标窗口处于活动状态

  4. 备份动作序列:定期备份actions.dat文件

9.2 安全提示

  1. 合法使用:请勿用于游戏作弊等非法用途

  2. 避免滥用:不要设置过快的点击频率,避免对系统造成负担

  3. 注意隐私:不要在涉及隐私信息的窗口使用自动点击

9.3 性能优化

  1. 减少动作数量:动作序列过长可能影响执行稳定性

  2. 适当增加延迟:在连续操作之间留出足够时间

  3. 关闭不必要的程序:执行时关闭其他占用资源的程序

9.4 故障处理

  1. 停止无效时:尝试多次按Ctrl+C或Esc

  2. 程序无响应:通过任务管理器结束进程

  3. 配置丢失:删除actions.dat文件后重新配置


十、更新日志

  • 版本1.0(当前版本)

    • 基础点击功能(单次、重复、无限循环)

    • 动作序列管理(添加、编辑、删除、执行)

    • 全局热键支持(Ctrl+C停止所有操作)

    • 进度可视化显示

    • 数据自动保存/加载

    • 快捷键支持(空格键拾取坐标等)

注意:本软件为免费工具,不提供技术支持。使用过程中请自行承担风险。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

cpp_learners

你的鼓励将是我创作的最大动力

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

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

打赏作者

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

抵扣说明:

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

余额充值