다른 명령
잔글 개발노트 양식 일괄 적용 |
|||
| (같은 사용자의 중간 판 하나는 보이지 않습니다) | |||
| 1번째 줄: | 1번째 줄: | ||
{{개발노트 | |||
|요약=Windows 에서 캐럿(커서) 위치의 텍스트를 출력하는 C++과 AutoIt ,Python 소스 예제입니다. | |||
|핵심= | |||
|분야=Python | |||
|작성일=2025-06-17 | |||
}} | |||
== Windows 에서 커서(Caret)가 위치 텍스트 출력 프로그램 == | == Windows 에서 커서(Caret)가 위치 텍스트 출력 프로그램 == | ||
| 122번째 줄: | 128번째 줄: | ||
<source lang=python> | <source lang=python> | ||
pip install pywinauto keyboard | |||
import | |||
import | import sys | ||
import threading | |||
import time | import time | ||
import tkinter as tk | |||
from tkinter import ttk | |||
import keyboard | |||
import pywinauto | |||
from pywinauto.keyboard import send_keys | |||
def | class CaretReaderApp: | ||
def __init__(self, root): | |||
self.root = root | |||
self.root.title("Caret Reader v1.1") | |||
self.root.geometry("320x130") | |||
self.root.attributes("-topmost", True) # 창을 항상 위에 유지 | |||
self.root.resizable(False, False) | |||
# | # UI 스타일 설정 | ||
style = ttk.Style() | |||
style.theme_use("clam") | |||
# | # UI 요소 배치 | ||
self.label_info = ttk.Label(root, text="[Ctrl + Shift + F12]를 누르면\n커서 위치의 문자를 읽습니다.", justify="center", font=("Malgun Gothic", 10)) | |||
self.label_info.pack(pady=12) | |||
self.label_result = ttk.Label(root, text="읽은 문자: 없음", font=("Malgun Gothic", 12, "bold"), foreground="#0078D7") | |||
self.label_result.pack(pady=5) | |||
# 단축키 리스너 등록 (데몬 스레드) | |||
self.hotkey_thread = threading.Thread(target=self.setup_hotkey, daemon=True) | |||
self.hotkey_thread.start() | |||
def setup_hotkey(self): | |||
# 전역 단축키 매핑 | |||
keyboard.add_hotkey("ctrl+shift+f12", self.trigger_caret_reading) | |||
keyboard.wait() | |||
def trigger_caret_reading(self): | |||
# 무거운 키보드/UI 매크로 작업 시 GUI 멈춤을 방지하기 위해 스레드 분리 | |||
threading.Thread(target=self.read_caret_pipeline, daemon=True).start() | |||
def read_caret_pipeline(self): | |||
print("\n[이벤트] 단축키 감지! 커서 위치 분석 시작...") | |||
# 1단계: 정석적인 UI Automation (UIA) 시도 | |||
character = self.try_uia_backend() | |||
# 2단계: UIA 실패 시, 질문하신 '커서 + 라이트키 + 클립보드' 매크로 보안책 가동 | |||
if not character: | |||
print("[UIA 실패] -> 커서 + Right Key 매크로 우회(Fallback)를 시작합니다.") | |||
character = self.try_cursor_right_fallback() | |||
# GUI 화면 레이블 업데이트 | |||
if character and character.strip() != "": | |||
cleaned_char = character.replace("\n", "\\n").replace("\r", "\\r") | |||
self.root.after(0, lambda: self.label_result.config(text=f"읽은 문자: '{cleaned_char}'")) | |||
print(f"[성공] 최종 가져온 문자: '{cleaned_char}'") | |||
else: | else: | ||
self.root.after(0, lambda: self.label_result.config(text="읽은 문자: 추출 실패")) | |||
print("[실패] 문자를 읽지 못했습니다.") | |||
return f" | |||
def try_uia_backend(self): | |||
try: | |||
# 활성화된 앱 내부에서 포커스된 컨트롤 찾기 | |||
app = pywinauto.Application(backend="uia").connect(active_only=True) | |||
focused_control = app.top_window().set_focus() | |||
text_pattern = focused_control.get_text_block() | |||
selection = text_pattern.get_selection() | |||
if selection: | |||
caret_range = selection | |||
caret_range.expand_to_enclosing_unit("Character") | |||
return caret_range.get_text() | |||
except Exception as e: | |||
# UIA를 안 만들어둔 일반 창이나 게임 등에서는 예외 발생 -> 자연스럽게 폴백으로 이동 | |||
pass | |||
return None | |||
def try_cursor_right_fallback(self): | |||
try: | |||
import win32clipboard | |||
# 기존 클립보드 문자열 백업 (유저 데이터 보호) | |||
original_text = "" | |||
try: | |||
win32clipboard.OpenClipboard() | |||
if win32clipboard.IsClipboardFormatAvailable(win32clipboard.CF_UNICODETEXT): | |||
original_text = win32clipboard.GetClipboardData(win32clipboard.CF_UNICODETEXT) | |||
win32clipboard.CloseClipboard() | |||
except Exception: | |||
pass | |||
# 1. 클립보드를 깔끔하게 비우기 (이전 데이터 오염 방지) | |||
win32clipboard.OpenClipboard() | |||
win32clipboard.EmptyClipboard() | |||
win32clipboard.CloseClipboard() | |||
# 2. Shift + Right Arrow 전송 (커서 기준 오른쪽 한 글자 블록 선택) | |||
send_keys("+{RIGHT}") | |||
time.sleep(0.06) # 타겟 프로그램이 텍스트 선택 영역을 갱신할 시간 부여 | |||
# 3. Ctrl + C 전송 (복사) | |||
send_keys("^c") | |||
time.sleep(0.06) # 클립보드에 데이터가 올라갈 때까지 대기 | |||
# 4. 클립보드에서 가로챈 문자열 열기 | |||
target_char = None | |||
win32clipboard.OpenClipboard() | |||
try: | |||
if win32clipboard.IsClipboardFormatAvailable(win32clipboard.CF_UNICODETEXT): | |||
target_char = win32clipboard.GetClipboardData(win32clipboard.CF_UNICODETEXT) | |||
except Exception as ce: | |||
print(f"클립보드 읽기 에러: {ce}") | |||
finally: | |||
win32clipboard.CloseClipboard() | |||
# 5. Left Arrow 전송 (블록 지정을 풀고 원래 커서 위치로 원위치 복구) | |||
send_keys("{LEFT}") | |||
time.sleep(0.02) | |||
# 6. 백업해 뒀던 원래 유저의 클립보드 데이터 복원 | |||
if original_text: | |||
try: | |||
win32clipboard.OpenClipboard() | |||
win32clipboard.EmptyClipboard() | |||
win32clipboard.SetClipboardText(original_text, win32clipboard.CF_UNICODETEXT) | |||
win32clipboard.CloseClipboard() | |||
except Exception: | |||
pass | |||
return target_char | |||
except Exception as e: | |||
print(f"매크로 로직 내부 에러: {e}") | |||
return None | |||
if __name__ == "__main__": | if __name__ == "__main__": | ||
# OS 전역 키 입력을 제어하므로 반드시 '관리자 권한'으로 실행해야 끊김이 없습니다. | |||
root = tk.Tk() | |||
app = CaretReaderApp(root) | |||
root.mainloop() | |||
</source> | </source> | ||
2026년 11월 12일 (목) 12:45 기준 최신판
DEVCAFE NOTE
PythonWindows 에서 캐럿(커서) 위치의 텍스트를 출력하는 C++과 AutoIt ,Python 소스 예제입니다.
KEY POINTS · 핵심 정리
Windows 에서 커서(Caret)가 위치 텍스트 출력 프로그램
- Windows 에서 캐럿(커서) 위치의 텍스트를 출력하는 C++과 AutoIt ,Python 소스 예제입니다.
C++ 버전
#include <windows.h>
#include <iostream>
#include <string>
std::string GetTextAroundCaret(HWND hWnd) {
DWORD startPos, endPos;
CHAR buffer[1024] = {0};
// 현재 포커스가 있는 윈도우 핸들 가져오기
if (hWnd == NULL) {
hWnd = GetFocus();
}
// Edit 컨트롤인지 확인
char className[256];
GetClassNameA(hWnd, className, sizeof(className));
if (strcmp(className, "Edit") != 0) {
return "Not an Edit control";
}
// 현재 선택 영역 가져오기
SendMessageA(hWnd, EM_GETSEL, (WPARAM)&startPos, (LPARAM)&endPos);
// 텍스트 길이 가져오기
int textLength = GetWindowTextLengthA(hWnd);
// 전체 텍스트 가져오기
GetWindowTextA(hWnd, buffer, textLength + 1);
// 캐럿 주변 텍스트 추출 (예: 캐럿 앞 10자, 뒤 10자)
int start = max(0, (int)startPos - 10);
int end = min(textLength, (int)startPos + 10);
return std::string(buffer + start, buffer + end);
}
int main() {
std::cout << "Caret 위치 텍스트를 가져오는 프로그램 (10초간 실행)" << std::endl;
for (int i = 0; i < 10; i++) {
HWND foregroundWindow = GetForegroundWindow();
HWND focusWindow = GetFocus();
std::string text = GetTextAroundCaret(focusWindow);
std::cout << "Caret 주변 텍스트: " << text << std::endl;
Sleep(1000); // 1초 대기
}
return 0;
}
AutoIt 버전
#include <WinAPI.au3>
#include <EditConstants.au3>
#include <Math.au3>
Func GetCaretText()
Local $hWnd = _WinAPI_GetFocus()
If $hWnd = 0 Then Return "포커스된 창이 없습니다."
; 클래스 이름 확인
Local $sClass = _WinAPI_GetClassName($hWnd)
If $sClass <> "Edit" Then Return "Edit 컨트롤이 아닙니다: " & $sClass
; 현재 선택 위치 가져오기
Local $iStart = _SendMessage($hWnd, $EM_GETSEL)
Local $iEnd = BitShift($iStart, 16)
$iStart = BitAND($iStart, 0xFFFF)
; 전체 텍스트 가져오기
Local $sText = ControlGetText("[ACTIVE]", "", $hWnd)
If @error Then Return "텍스트를 가져올 수 없습니다."
; 캐럿 주변 텍스트 추출 (앞 10자, 뒤 10자)
Local $iTextLen = StringLen($sText)
Local $iFrom = _Max(0, $iStart - 10)
Local $iTo = _Min($iTextLen, $iStart + 10)
Return StringMid($sText, $iFrom + 1, $iTo - $iFrom)
EndFunc
; 메인 프로그램
ToolTip("캐럿 위치 텍스트를 가져오는 AutoIt 프로그램 (10초간 실행)", 0, 0)
For $i = 1 To 10
Local $sText = GetCaretText()
ConsoleWrite("캐럿 주변 텍스트: " & $sText & @CRLF)
Sleep(1000)
Next
Exit
프로그램 사용 방법
- C++ 버전:
- - Visual Studio 등으로 컴파일 후 실행
- - 10초 동안 1초 간격으로 포커스된 Edit 컨트롤의 캐럿 위치 주변 텍스트 출력
- AutoIt 버전:
- - AutoIt 스크립트로 저장 후 실행
- C++ 버전:
++: - 10초 동안 1초 간격으로 콘솔에 캐럿 위치 주변 텍스트 출력
주의 사항
- - 이 프로그램은 표준 Edit 컨트롤에서만 작동합니다.
- - RichEdit, Chrome, 워드프로세서 등 다른 컨트롤에서는 작동하지 않을 수 있습니다.
- - 관리자 권한이 필요한 경우도 있을 수 있습니다.
Python 버전
- pywin32 라이브러리 (pip install pywin32) 설치
pip install pywinauto keyboard
import sys
import threading
import time
import tkinter as tk
from tkinter import ttk
import keyboard
import pywinauto
from pywinauto.keyboard import send_keys
class CaretReaderApp:
def __init__(self, root):
self.root = root
self.root.title("Caret Reader v1.1")
self.root.geometry("320x130")
self.root.attributes("-topmost", True) # 창을 항상 위에 유지
self.root.resizable(False, False)
# UI 스타일 설정
style = ttk.Style()
style.theme_use("clam")
# UI 요소 배치
self.label_info = ttk.Label(root, text="[Ctrl + Shift + F12]를 누르면\n커서 위치의 문자를 읽습니다.", justify="center", font=("Malgun Gothic", 10))
self.label_info.pack(pady=12)
self.label_result = ttk.Label(root, text="읽은 문자: 없음", font=("Malgun Gothic", 12, "bold"), foreground="#0078D7")
self.label_result.pack(pady=5)
# 단축키 리스너 등록 (데몬 스레드)
self.hotkey_thread = threading.Thread(target=self.setup_hotkey, daemon=True)
self.hotkey_thread.start()
def setup_hotkey(self):
# 전역 단축키 매핑
keyboard.add_hotkey("ctrl+shift+f12", self.trigger_caret_reading)
keyboard.wait()
def trigger_caret_reading(self):
# 무거운 키보드/UI 매크로 작업 시 GUI 멈춤을 방지하기 위해 스레드 분리
threading.Thread(target=self.read_caret_pipeline, daemon=True).start()
def read_caret_pipeline(self):
print("\n[이벤트] 단축키 감지! 커서 위치 분석 시작...")
# 1단계: 정석적인 UI Automation (UIA) 시도
character = self.try_uia_backend()
# 2단계: UIA 실패 시, 질문하신 '커서 + 라이트키 + 클립보드' 매크로 보안책 가동
if not character:
print("[UIA 실패] -> 커서 + Right Key 매크로 우회(Fallback)를 시작합니다.")
character = self.try_cursor_right_fallback()
# GUI 화면 레이블 업데이트
if character and character.strip() != "":
cleaned_char = character.replace("\n", "\\n").replace("\r", "\\r")
self.root.after(0, lambda: self.label_result.config(text=f"읽은 문자: '{cleaned_char}'"))
print(f"[성공] 최종 가져온 문자: '{cleaned_char}'")
else:
self.root.after(0, lambda: self.label_result.config(text="읽은 문자: 추출 실패"))
print("[실패] 문자를 읽지 못했습니다.")
def try_uia_backend(self):
try:
# 활성화된 앱 내부에서 포커스된 컨트롤 찾기
app = pywinauto.Application(backend="uia").connect(active_only=True)
focused_control = app.top_window().set_focus()
text_pattern = focused_control.get_text_block()
selection = text_pattern.get_selection()
if selection:
caret_range = selection
caret_range.expand_to_enclosing_unit("Character")
return caret_range.get_text()
except Exception as e:
# UIA를 안 만들어둔 일반 창이나 게임 등에서는 예외 발생 -> 자연스럽게 폴백으로 이동
pass
return None
def try_cursor_right_fallback(self):
try:
import win32clipboard
# 기존 클립보드 문자열 백업 (유저 데이터 보호)
original_text = ""
try:
win32clipboard.OpenClipboard()
if win32clipboard.IsClipboardFormatAvailable(win32clipboard.CF_UNICODETEXT):
original_text = win32clipboard.GetClipboardData(win32clipboard.CF_UNICODETEXT)
win32clipboard.CloseClipboard()
except Exception:
pass
# 1. 클립보드를 깔끔하게 비우기 (이전 데이터 오염 방지)
win32clipboard.OpenClipboard()
win32clipboard.EmptyClipboard()
win32clipboard.CloseClipboard()
# 2. Shift + Right Arrow 전송 (커서 기준 오른쪽 한 글자 블록 선택)
send_keys("+{RIGHT}")
time.sleep(0.06) # 타겟 프로그램이 텍스트 선택 영역을 갱신할 시간 부여
# 3. Ctrl + C 전송 (복사)
send_keys("^c")
time.sleep(0.06) # 클립보드에 데이터가 올라갈 때까지 대기
# 4. 클립보드에서 가로챈 문자열 열기
target_char = None
win32clipboard.OpenClipboard()
try:
if win32clipboard.IsClipboardFormatAvailable(win32clipboard.CF_UNICODETEXT):
target_char = win32clipboard.GetClipboardData(win32clipboard.CF_UNICODETEXT)
except Exception as ce:
print(f"클립보드 읽기 에러: {ce}")
finally:
win32clipboard.CloseClipboard()
# 5. Left Arrow 전송 (블록 지정을 풀고 원래 커서 위치로 원위치 복구)
send_keys("{LEFT}")
time.sleep(0.02)
# 6. 백업해 뒀던 원래 유저의 클립보드 데이터 복원
if original_text:
try:
win32clipboard.OpenClipboard()
win32clipboard.EmptyClipboard()
win32clipboard.SetClipboardText(original_text, win32clipboard.CF_UNICODETEXT)
win32clipboard.CloseClipboard()
except Exception:
pass
return target_char
except Exception as e:
print(f"매크로 로직 내부 에러: {e}")
return None
if __name__ == "__main__":
# OS 전역 키 입력을 제어하므로 반드시 '관리자 권한'으로 실행해야 끊김이 없습니다.
root = tk.Tk()
app = CaretReaderApp(root)
root.mainloop()
코드 설명
- 필요한 모듈: pywin32를 사용하여 Windows API를 호출합니다.
- 포커스된 창 확인: GetForegroundWindow로 현재 포커스된 창의 핸들을 가져옵니다.
- 텍스트 컨트롤 확인: 창의 클래스 이름이 Edit 또는 RichEdit인지 확인하여 텍스트 입력 창인지 판단합니다.
- 텍스트 가져오기: WM_GETTEXT 메시지로 창의 전체 텍스트를 가져오고, EM_GETSEL 메시지로 캐럿의 시작/끝 위치를 가져옵니다.
- 캐럿 주변 텍스트 추출: 캐럿 위치를 기준으로 앞뒤 10자 내의 텍스트를 추출합니다.
- 실행: 프로그램 실행 후 5초 대기하여 사용자가 원하는 텍스트 창(예: 메모장)에 포커스를 맞출 시간을 줍니다.
사용 방법
- Python 환경에 pywin32를 설치합니다:
pip install pywin32
- 위 코드를 .py 파일로 저장합니다(예:caret_text.py).
- 메모장이나 다른 텍스트 편집기(예: Notepad++, Word 등)를 열고 텍스트를 입력한 뒤 커서를 원하는 위치에 놓습니다.
- 코드를 실행하면 5초 후 현재 캐럿 위치 주변의 텍스트와 전체 텍스트를 출력합니다.
주의사항
- 이 코드는 Windows의 기본 텍스트 편집 컨트롤(Edit, RichEdit)에서 작동합니다. 일부 응용프로그램(예: 웹 브라우저의 텍스트 입력란)은 다른 방식으로 구현될 수 있어 작동하지 않을 수 있습니다.
- 웹 브라우저나 특수 응용프로그램의 경우, 추가로 UIAutomation이나 pyautogui 같은 라이브러리가 필요할 수 있습니다.
- 코드 실행 전, 포커스된 창이 텍스트 입력 창인지 확인하세요(예: 메모장).
pywinauto 이용
만약 더 복잡한 응용프로그램(예: 브라우저)에서 캐럿 위치의 텍스트를 가져오고 싶다면, Microsoft의 UI Automation 프레임워크를 사용할 수 있습니다. 아래는 pywinauto를 사용한 간단한 예제입니다:
from pywinauto import Application
import time
def get_caret_text_uia():
try:
app = Application().connect(focused=True)
focused_window = app.window(focused=True)
# 텍스트 컨트롤 찾기
text_control = focused_window.child_window(control_type="Edit")
if text_control.exists():
text = text_control.window_text()
caret_pos = text_control.get_selection()[0]
caret_text = text[max(0, caret_pos-10):caret_pos+10]
return f"캐럿 위치 텍스트: {caret_text}\n전체 텍스트: {text}\n캐럿 위치: {caret_pos}"
else:
return "텍스트 컨트롤을 찾을 수 없습니다."
except Exception as e:
return f"오류 발생: {str(e)}"
if __name__ == "__main__":
print("5초 후에 현재 캐럿 위치의 텍스트를 조회합니다...")
time.sleep(5)
result = get_caret_text_uia()
print(result)
- 추가 설치
pip install pywinauto
참고
- pywin32 방식은 가볍고 빠르지만 기본 Windows 컨트롤에 한정됩니다.
- pywinauto는 더 복잡한 응용프로그램에서 작동할 가능성이 높지만 설정이 더 복잡할 수 있습니다.