반응형
다운로드는 ↓ ↓ ↓
https://tears2am.tistory.com/67
import pyautogui
import time
import threading
import tkinter as tk
from tkinter import simpledialog, messagebox
import keyboard
import csv
import os
from pynput import mouse
click_positions = []
# 기본 딜레이
delay_s = 0
delay_ds = 0
delay_cs = 0
clicking = False
click_thread = None
save_folder = "저장된 좌표 목록"
if not os.path.exists(save_folder):
os.makedirs(save_folder)
def get_default_delay():
try:
s = float(entry_s.get()) if entry_s.get() else 0
ds = float(entry_ds.get()) if entry_ds.get() else 0
cs = float(entry_cs.get()) if entry_cs.get() else 0
except ValueError:
s, ds, cs = 0, 0, 0
return s + (ds * 0.1) + (cs * 0.01)
def record_position(insert_at=None, pos=None):
if pos is None:
pos = pyautogui.position()
delay = get_default_delay()
data = (pos[0], pos[1], delay)
if insert_at is not None:
click_positions.insert(insert_at, data)
else:
click_positions.append(data)
update_position_list()
status_label.config(text=f"좌표 기록됨: {pos} (딜레이: {delay}초)")
def update_position_list():
pos_listbox.delete(0, tk.END)
for idx, (x, y, d) in enumerate(click_positions):
pos_listbox.insert(tk.END, f"{idx}: ({x}, {y}) - 딜레이 {d:.2f}초")
def update_file_list():
file_listbox.delete(0, tk.END)
files = [f for f in os.listdir(save_folder) if f.endswith(".csv")]
for f in files:
file_listbox.insert(tk.END, f)
def start_clicking():
global clicking, click_thread
if not click_positions:
messagebox.showwarning("좌표 없음", "기록된 좌표가 없습니다.")
return
if clicking:
return
clicking = True
click_thread = threading.Thread(target=click_loop, daemon=True)
click_thread.start()
status_label.config(text="클릭 시작됨")
def stop_clicking():
global clicking
clicking = False
status_label.config(text="클릭 중지됨")
def click_loop():
index = 0
while clicking:
if not click_positions:
break
x, y, delay = click_positions[index]
time.sleep(delay)
pyautogui.moveTo(x, y)
pyautogui.click()
print(f"클릭 위치: ({x}, {y}), 딜레이: {delay}초")
index = (index + 1) % len(click_positions)
def adjust_delay(entry, delta):
value = entry.get()
try:
value = int(value) if value else 0
except ValueError:
value = 0
value += delta
value = max(0, value)
entry.delete(0, tk.END)
entry.insert(0, str(value))
def edit_position_delay(event):
selection = pos_listbox.curselection()
if selection:
index = selection[0]
x, y, old_delay = click_positions[index]
new_delay = simpledialog.askfloat("딜레이 수정", f"현재 딜레이: {old_delay:.2f}초\n새 딜레이 입력:", initialvalue=old_delay)
if new_delay is not None:
click_positions[index] = (x, y, new_delay)
update_position_list()
# 전역 마우스 클릭으로 좌표 삽입
def insert_position():
selection = pos_listbox.curselection()
if selection:
insert_at = selection[0]
else:
insert_at = None
status_label.config(text="원하는 위치를 클릭하세요 (ESC로 취소)")
def on_click(x, y, button, pressed):
if pressed and button == mouse.Button.left:
listener.stop()
record_position(insert_at=insert_at, pos=(x, y))
status_label.config(text="좌표 삽입 완료")
listener = mouse.Listener(on_click=on_click)
listener.start()
def delete_position():
selection = pos_listbox.curselection()
if selection:
index = selection[0]
del click_positions[index]
update_position_list()
def delete_all():
global click_positions
click_positions = []
update_position_list()
def save_positions():
filename = simpledialog.askstring("파일 이름 입력", "저장할 파일 이름(확장자는 자동 추가됩니다):")
if filename:
filepath = os.path.join(save_folder, f"{filename}.csv")
if os.path.exists(filepath):
overwrite = messagebox.askyesno("파일 덮어쓰기", f"{filename}.csv 파일이 이미 존재합니다.\n덮어쓰시겠습니까?")
if not overwrite:
status_label.config(text="저장 취소됨")
return
with open(filepath, "w", newline="") as f:
writer = csv.writer(f)
for x, y, delay in click_positions:
writer.writerow([x, y, delay])
status_label.config(text=f"저장됨: {filename}.csv")
update_file_list()
def load_selected_file(event):
selection = file_listbox.curselection()
if selection:
filename = file_listbox.get(selection[0])
filepath = os.path.join(save_folder, filename)
load_positions(filepath)
def load_positions(filepath):
global click_positions
with open(filepath, "r") as f:
reader = csv.reader(f)
click_positions = []
for row in reader:
x, y, delay = float(row[0]), float(row[1]), float(row[2])
click_positions.append((x, y, delay))
update_position_list()
status_label.config(text=f"불러온 파일: {os.path.basename(filepath)}")
def delete_selected_file():
selection = file_listbox.curselection()
if selection:
filename = file_listbox.get(selection[0])
filepath = os.path.join(save_folder, filename)
if messagebox.askyesno("파일 삭제", f"{filename} 파일을 삭제하시겠습니까?"):
os.remove(filepath)
update_file_list()
status_label.config(text=f"파일 삭제됨: {filename}")
def new_file():
global click_positions
click_positions = []
update_position_list()
status_label.config(text="새 리스트 생성됨")
def move_up():
selection = pos_listbox.curselection()
if selection and selection[0] > 0:
idx = selection[0]
click_positions[idx-1], click_positions[idx] = click_positions[idx], click_positions[idx-1]
update_position_list()
pos_listbox.select_set(idx-1)
def move_down():
selection = pos_listbox.curselection()
if selection and selection[0] < len(click_positions) - 1:
idx = selection[0]
click_positions[idx+1], click_positions[idx] = click_positions[idx], click_positions[idx+1]
update_position_list()
pos_listbox.select_set(idx+1)
# 🖥 GUI 시작 전에 좌표 리스트 무조건 초기화
click_positions.clear()
# 🖥 GUI
root = tk.Tk()
root.title("마우스 오토 클릭")
root.resizable(False, False) # 창 크기 고정!
# === 딜레이 설정 ===
delay_frame = tk.LabelFrame(root, text="기본 딜레이 설정", padx=5, pady=5)
delay_frame.grid(row=0, column=0, sticky="w", padx=10, pady=5)
tk.Label(delay_frame, text="초 (s):").grid(row=0, column=0)
entry_s = tk.Entry(delay_frame, width=5)
entry_s.insert(0, str(delay_s))
entry_s.grid(row=0, column=1)
tk.Button(delay_frame, text="+", command=lambda: adjust_delay(entry_s, 1)).grid(row=0, column=2)
tk.Button(delay_frame, text="-", command=lambda: adjust_delay(entry_s, -1)).grid(row=0, column=3)
tk.Label(delay_frame, text="1/10초:").grid(row=0, column=4)
entry_ds = tk.Entry(delay_frame, width=5)
entry_ds.insert(0, str(delay_ds))
entry_ds.grid(row=0, column=5)
tk.Button(delay_frame, text="+", command=lambda: adjust_delay(entry_ds, 1)).grid(row=0, column=6)
tk.Button(delay_frame, text="-", command=lambda: adjust_delay(entry_ds, -1)).grid(row=0, column=7)
tk.Label(delay_frame, text="1/100초:").grid(row=0, column=8)
entry_cs = tk.Entry(delay_frame, width=5)
entry_cs.insert(0, str(delay_cs))
entry_cs.grid(row=0, column=9)
tk.Button(delay_frame, text="+", command=lambda: adjust_delay(entry_cs, 1)).grid(row=0, column=10)
tk.Button(delay_frame, text="-", command=lambda: adjust_delay(entry_cs, -1)).grid(row=0, column=11)
# === 파일 목록 (오른쪽에 세로 길게 배치) ===
file_frame = tk.LabelFrame(root, text="저장된 파일 목록")
file_frame.grid(row=0, column=1, rowspan=5, padx=10, pady=5, sticky="ns")
file_listbox = tk.Listbox(file_frame, width=30, height=20)
file_listbox.pack()
file_listbox.bind("<Double-Button-1>", load_selected_file)
btn_delete_file = tk.Button(file_frame, text="선택 파일 삭제", command=delete_selected_file)
btn_delete_file.pack(pady=5)
# === 좌표 리스트 상단 버튼 프레임 ===
top_btn_frame = tk.Frame(root)
top_btn_frame.grid(row=1, column=0, sticky="w", padx=10, pady=2)
tk.Button(top_btn_frame, text="새로 만들기", width=12, command=new_file).grid(row=0, column=0, padx=2)
tk.Button(top_btn_frame, text="저장", width=12, command=save_positions).grid(row=0, column=1, padx=2)
# === 좌표 리스트 위 라벨 ===
tk.Label(root, text="기록된 좌표 (더블 클릭으로 딜레이 수정):").grid(row=2, column=0, sticky="w", padx=10)
# === 좌표 리스트 ===
pos_listbox = tk.Listbox(root, width=60, height=10)
pos_listbox.grid(row=3, column=0, padx=10)
pos_listbox.bind("<Double-Button-1>", edit_position_delay)
# === 좌표 리스트 아래 버튼 프레임 ===
btn_frame = tk.Frame(root)
btn_frame.grid(row=4, column=0, pady=5)
tk.Button(btn_frame, text="선택 위치 삽입", width=13, command=insert_position).grid(row=0, column=0)
tk.Button(btn_frame, text="선택 삭제", width=10, command=delete_position).grid(row=0, column=1)
tk.Button(btn_frame, text="전체 삭제", width=10, command=delete_all).grid(row=0, column=2)
tk.Button(btn_frame, text="위로 이동", width=10, command=move_up).grid(row=0, column=3)
tk.Button(btn_frame, text="아래로 이동", width=10, command=move_down).grid(row=0, column=4)
# === 상태 표시 ===
status_label = tk.Label(root, text="상태: 준비됨", fg="blue")
status_label.grid(row=5, column=0, columnspan=2, pady=5)
update_position_list()
update_file_list()
# === 핫키 ===
def listen_hotkeys():
keyboard.add_hotkey('F2', record_position)
keyboard.add_hotkey('F3', start_clicking)
keyboard.add_hotkey('F4', stop_clicking)
print("""
[F2] → 현재 마우스 좌표 기록 (맨 끝에 추가)
[F3] → 클릭 시작
[F4] → 클릭 중지
""")
while True:
time.sleep(0.1)
hotkey_thread = threading.Thread(target=listen_hotkeys, daemon=True)
hotkey_thread.start()
root.mainloop()반응형
'일기장 > 일상' 카테고리의 다른 글
| MS 오피스21 CMD 인증 (2) | 2025.08.05 |
|---|---|
| 마우스 오토 클릭 프로그램 (0) | 2025.05.06 |
| 2024 지스타(G-STAR) 컨퍼런스 현장에 가다 (1) | 2024.11.18 |