`event` — 가상 이벤트 정의하고 이벤트 생성하기
event — 가상 이벤트 정의하고 이벤트 생성하기
서로 다른 운영체제에서 같은 동작(복사, 붙여넣기 등)을 각자의 단축키로 하다 보면, 바인딩 코드가 플랫폼마다 갈라져요. Tk는 가상 이벤트(virtual event) 로 그 단축키들을 하나로 묶어 주고, event generate로 실제 키·마우스 이벤트를 프로그램이 스스로 만들어 낼 수도 있게 해 줘요.
본문
시그니처
event option ?arg arg ...?
event 명령은 가상 이벤트 정의와 이벤트 합성 등 윈도우 시스템 이벤트를 다루는 여러 기능을 제공해요. 첫 인자에 따라 형태가 나뉘며, 현재 지원되는 형태는 다음과 같아요.
event add <<virtual>> sequence ?sequence ...?
event delete <<virtual>> ?sequence sequence ...?
event generate window event ?option value option value ...?
event info ?<<virtual>>?
하위 명령
event add <<virtual>> sequence ?sequence ...? — 가상 이벤트 virtual을 sequence 인자들이 주는 물리 이벤트 시퀀스와 연결해서, 그중 어느 시퀀스가 발생하든 가상 이벤트가 발동되게 해요. virtual은 아무 문자열 값이고, sequence는 bind 명령의 시퀀스 인자에 허용되는 값 중 아무거나 될 수 있어요. virtual이 이미 정의돼 있으면 새 물리 시퀀스가 기존 시퀀스에 추가돼요.
event delete <<virtual>> ?sequence sequence ...? — virtual 가상 이벤트와 연결된 시퀀스 중 각각을 삭제해요. virtual은 아무 문자열, sequence는 bind 시퀀스 인자에 허용되는 값이면 됩니다. virtual에 현재 연결되지 않은 시퀀스는 무시돼요. sequence 인자를 주지 않으면 virtual의 모든 물리 시퀀스를 제거해서 가상 이벤트가 더 이상 발동하지 않게 해요.
event generate window event ?option value option value ...? — 창 이벤트를 생성하고, 마치 윈도우 시스템에서 온 것처럼 처리되게 해요. window는 이벤트가 생성될 창의 path name이며, 현재 애플리케이션의 창이면 winfo id가 반환하는 것 같은 식별자여도 돼요. event는 <Shift-Button-2>나 <<Paste>> 같은 이벤트의 기본 설명이에요. window가 비어 있으면 화면 전체를 뜻하고 좌표는 화면 기준이에요. event는 bind 명령의 시퀀스 인자에 허용되는 형태 중 아무거나 될 수 있지만, 하나의 단일 이벤트 패턴이어야 하고 시퀀스는 안 돼요.
옵션-값 쌍으로 x·y 마우스 위치 같은 이벤트의 추가 속성을 지정할 수 있어요(아래 EVENT FIELDS). -when 옵션을 지정하지 않으면 이벤트는 즉시 처리돼요. 즉 event generate 명령이 반환되기 전에 모든 핸들러가 완료돼요. -when을 지정하면 그 값이 언제 처리할지 결정해요. 키 이벤트 같은 일부 이벤트는 이벤트를 제대로 받으려면 창이 포커스를 가져야 해요.
event info ?<<virtual>>? — 가상 이벤트에 대한 정보를 돌려줘요. <<virtual>>을 생략하면 현재 정의된 모든 가상 이벤트 목록을 돌려줘요. 지정하면 그 가상 이벤트에 현재 정의된 물리 이벤트 시퀀스 목록을 돌려주고, 정의돼 있지 않으면 빈 문자열을 돌려줘요. 물리 이벤트 시퀀스에 바인딩되지 않은 가상 이벤트는 event info가 돌려주지 않아요.
이벤트 필드(EVENT FIELDS)
event generate 명령에 지원되는 옵션이에요. bind 명령의 바인딩 스크립트에서 허용되는 "%" 확장에 대응해요.
-above window— 이벤트의above필드. 창 path name이나 정수 창 id로 지정. Configure 이벤트에 유효. 바인딩 스크립트의%a치환에 해당.-borderwidth size—border_width필드. 화면 거리여야 함. Configure 이벤트에 유효.%B에 해당.-button number—detail필드. 정수여야 하며, ButtonPress·ButtonRelease 이벤트의 기본 이벤트 인자에 있는 버튼 번호를 덮어씀.%b에 해당.-count number—count필드. 정수. Expose 이벤트에 유효.%c에 해당.-data string—user_data필드. 아무 값. 가상 이벤트에만 유효. 가상 이벤트의%d치환에 해당.-delta number— MouseWheel 이벤트의delta필드. 정수. 마우스 휠이 회전한 방향·크기를 뜻하지만 화면 거리가 아니라 마우스 휠의 이동 단위예요. 보통 120의 배수예요. 예: 120은 text 위젯을 4줄 위로, -240은 8줄 아래로 스크롤.%D에 해당.-detail detail—detail필드로, 다음 중 하나여야 해요:NotifyAncestor,NotifyNonlinearVirtual,NotifyDetailNone,NotifyPointer,NotifyInferior,NotifyPointerRoot,NotifyNonlinear,NotifyVirtual. Enter·Leave·FocusIn·FocusOut 이벤트에 유효.%d에 해당.-focus boolean—focus필드. Enter·Leave 이벤트에 유효.%f에 해당.-height size—height필드. 화면 거리. Configure 이벤트에 유효.%h에 해당.-keycode number—keycode필드. 정수. KeyPress·KeyRelease 이벤트에 유효.%k에 해당.-keysym name— 유효한 keysym 이름(예:g,space,Return)이고, 그 대응 keycode 값이 이벤트의 keycode 필드로 쓰여 기본 이벤트 인자의 detail을 덮어씀. KeyPress·KeyRelease에 유효.%K에 해당.-mode notify—mode필드로NotifyNormal,NotifyGrab,NotifyUngrab,NotifyWhileGrabbed중 하나. Enter·Leave·FocusIn·FocusOut에 유효.%m에 해당.-override boolean—override_redirect필드. Map·Reparent·Configure 이벤트에 유효.%o에 해당.-place where—place필드로PlaceOnTop또는PlaceOnBottom. Circulate 이벤트에 유효.%p에 해당.-root window—root필드. 창 path name 또는 정수 창 id. KeyPress·KeyRelease·ButtonPress·ButtonRelease·Enter·Leave·Motion 이벤트에 유효.%R에 해당.-rootx coord—x_root필드. 화면 거리. 위 동일 이벤트들에 유효.%X에 해당.-rooty coord—y_root필드.%Y에 해당.-sendevent boolean—send_event필드. 모든 이벤트에 유효.%E에 해당.-serial number—serial필드. 정수. 모든 이벤트에 유효.%#에 해당.-state state—state필드. KeyPress·KeyRelease·ButtonPress·ButtonRelease·Enter·Leave·Motion에서는 정수여야 하고, Visibility 이벤트에서는VisibilityUnobscured·VisibilityPartiallyObscured·VisibilityFullyObscured중 하나여야 해요. 기본 이벤트에 지정된 Meta·Control 같은 수정자를 덮어씀.%s에 해당.-subwindow window—subwindow필드. Tk 위젯 path name 또는 정수 창 id. 위 동일 이벤트들에 유효.%S와 유사.-time integer—time필드. 정수. 특수값current도 허용되며 이 값은 현재 이벤트 시간으로 대체돼요. KeyPress·KeyRelease·ButtonPress·ButtonRelease·Enter·Leave·Motion·Property 이벤트에 유효.%t에 해당.-warp boolean— 화면 포인터도 함께 warp할지. KeyPress·KeyRelease·ButtonPress·ButtonRelease·Motion에 유효. Tk는 화면 root 창 기준, 그리고 Tk 창 기준 포인터 warp를 지원해요. 후자의 경우 창이 mapped일 때만 warp돼요.-width size—width필드. 화면 거리. Configure 이벤트에 유효.%w에 해당.-when when— 이벤트 처리 시점. 다음 중 하나:now— 명령이 반환되기 전에 즉시 처리.-when을 생략해도 같음.tail— 이 애플리케이션에 이미 대기 중인 이벤트들 뒤로 Tcl 이벤트 큐에 넣음.head— 이미 대기 중인 다른 이벤트보다 먼저 처리되도록 큐 앞에 넣음.mark— 큐 앞에 넣되,-when mark로 이미 넣은 다른 이벤트 뒤로. 순서대로 처리해야 하지만 큐 앞에 놓아야 하는 이벤트 시리즈 생성에 유용.
-x coord—x필드. 화면 거리. KeyPress·KeyRelease·ButtonPress·ButtonRelease·Motion·Enter·Leave·Expose·Configure·Gravity·Reparent에 유효.%x에 해당.window가 비어 있으면 좌표는 화면 기준이고%X에 해당.-y coord—y필드.%y에 해당.window가 비어 있으면 화면 기준이고%Y에 해당.
이벤트를 생성할 때 지정하지 않은 옵션은 0으로 채워져요. 단 serial은 다음 X 이벤트 시리얼 번호로 채워져요.
미리 정의된 가상 이벤트(PREDEFINED VIRTUAL EVENTS)
Tk는 알림 목적으로 다음 가상 이벤트를 정의해요.
<<AltUnderlined>>—-underline옵션으로 밑줄 그은(가속기 표시) 글자를 Alt 키와 함께 눌렀을 때 위젯에 전송. 보통 응답은 위젯(또는 관련 위젯)으로 포커스를 주거나 위젯을 호출하는 것.<<Invoke>>— 일부 위젯(button, listbox, menu 등)에<space>의 대안으로 보낼 수 있음.<<ListboxSelect>>— listbox의 선택 항목 집합이 갱신될 때 listbox에 전송.<<MenuSelect>>— 메뉴의 현재 선택 항목이 바뀔 때 메뉴에 전송. 상황별 도움말 시스템에 쓰기 위한 것.<<Modified>>— text 위젯의 내용이 바뀔 때 그 위젯에 전송.<<Selection>>— text 위젯의 선택이 바뀔 때 그 위젯에 전송.<<ThemeChanged>>— ttt theme가 바뀔 때 모든 위젯에 전송. ttk 위젯은 이 이벤트를 듣고 다시 그리지만, 레거시 위젯은 무시.<<TkWorldChanged>>— (예:[font configure]로) 폰트가 바뀔 때 모든 위젯에 전송.user_data필드(%d)는"FontChanged"값. 그 외 시스템 전반 변경에도 보내지며 user_data가 원인을 나타내요. 모든 tk·ttk 위젯은 이미 내부적으로 이 이벤트를 처리함.<<TraverseIn>>— 사용자 주도 "tab to widget" 동작으로 포커스가 위젯에 들어올 때 전송.<<TraverseOut>>— 사용자 주도 tab 동작으로 포커스가 위젯에서 떠날 때 전송.<<UndoStack>>— text 위젯의 undo·redo 스택이 비거나 차게 될 때 그 위젯에 전송.<<WidgetViewSync>>— text 위젯의 내부 데이터가 낡았을 때, 그리고 다시 위젯 보기와 동기화될 때 전송.detail필드(%d치환)는 동기화되면true, 아니면false.
또한 Tk는 여러 플랫폼에서 바인딩을 통일하기 위해 다음 가상 이벤트를 정의해요. 사용자들은 다음과 같이 동작할 것으로 기대해요.
<<Clear>>— 현재 선택된 위젯 내용 삭제.<<Copy>>— 현재 선택된 위젯 내용을 클립보드로 복사.<<Cut>>— 현재 선택된 위젯 내용을 클립보드로 이동(잘라내기).<<LineEnd>>— 선택 해제하며 현재 위젯의 줄 끝으로.<<LineStart>>— 선택 해제하며 현재 위젯의 줄 시작으로.<<NextChar>>— 선택 해제하며 다음 항목(보이는 문자)으로.<<NextLine>>— 선택 해제하며 다음 줄로.<<NextPara>>— 선택 해제하며 다음 문단으로.<<NextWord>>— 선택 해제하며 다음 항목 묶음(보이는 단어)으로.<<Paste>>— 현재 선택된 위젯 내용을 클립보드 내용으로 교체.<<PasteSelection>>— 마우스 위치에 선택 내용 삽입(이 이벤트는 의미 있는%x·%y치환을 가짐).<<PrevChar>>— 선택 해제하며 이전 항목으로.<<PrevLine>>— 선택 해제하며 이전 줄로.<<PrevPara>>— 선택 해제하며 이전 문단으로.<<PrevWindow>>— 이전 창으로 순회.<<PrevWord>>— 선택 해제하며 이전 항목 묶음으로.<<Redo>>— 실행 취소한 동작 하나 다시 실행.<<SelectAll>>— 선택 범위를 위젯 전체로.<<SelectLineEnd>>— 선택 범위를 유지하며 줄 끝으로.<<SelectLineStart>>— 선택 범위를 유지하며 줄 시작으로.<<SelectNextChar>>— 선택 범위를 유지하며 다음 항목으로.<<SelectNextLine>>— 선택 범위를 유지하며 다음 줄로.<<SelectNextPara>>— 선택 범위를 유지하며 다음 문단으로.<<SelectNextWord>>— 선택 범위를 유지하며 다음 항목 묶음으로.<<SelectNone>>— 선택 범위를 비움.<<SelectPrevChar>>— 선택 범위를 유지하며 이전 항목으로.<<SelectPrevLine>>— 선택 범위를 유지하며 이전 줄로.<<SelectPrevPara>>— 선택 범위를 유지하며 이전 문단으로.<<SelectPrevWord>>— 선택 범위를 유지하며 이전 항목 묶음으로.<<ToggleSelection>>— 선택 토글.<<Undo>>— 마지막 동작 실행 취소.
예제
키를 가상 이벤트에 매핑하기
가상 이벤트 바인딩이 발동하려면 두 가지가 필요해요. 첫째, event add로 가상 이벤트를 정의하고, 둘째, bind 명령으로 그 가상 이벤트에 바인딩을 만들어야 해요.
event add <<Paste>> <Control-y>
event add <<Paste>> <Button-2>
event add <<Save>> <Control-X><Control-S>
event add <<Save>> <Shift-F12>
if {[tk windowingsystem] eq "aqua"} {
event add <<Save>> <Command-s>
}
bind 명령에서 가상 이벤트는 다른 내장 이벤트 타입처럼 바인딩할 수 있어요.
bind Entry <<Paste>> {%W insert [selection get]}
겹꺾쇠(<<...>>)는 가상 이벤트를 바인딩한다는 뜻이에요. 사용자가 Control-y를 치거나 버튼 2를 누르거나, event generate로 <<Paste>> 가상 이벤트를 합성하면 <<Paste>> 바인딩이 호출돼요.
가상 바인딩이 별도의 물리 바인딩과 정확히 같은 시퀀스를 가지면, 물리 바인딩이 우선해요.
event add <<Paste>> <Control-y> <Meta-Control-y>
bind Entry <Control-y> {puts Control-y}
bind Entry <<Paste>> {puts Paste}
사용자가 Control-y를 치면 <Control-y> 바인딩이 호출돼요. 물리 이벤트가 다른 조건이 같을 때 가상 이벤트보다 더 구체적으로 간주되기 때문이에요. 하지만 사용자가 Meta-Control-y를 치면 <<Paste>> 바인딩이 호출돼요. 가상 바인딩과 연결된 물리 패턴의 Meta 수정자가 물리 이벤트의 <Control-y> 시퀀스보다 더 구체적이기 때문이에요.
가상 이벤트에 대한 바인딩은 그 가상 이벤트가 생기기 전에도 만들 수 있어요. 실제로는 특정 가상 이벤트가 무의미하거나 생성 불가능한 플랫폼에서는 그 이벤트가 정의될 필요조차 없어요. 실행 중에 가상 이벤트 정의가 바뀌면 모든 창이 즉시 새 정의에 반응해요.
bind Entry <Control-y> {}
event add <<Paste>> <Key-F6>
이렇게 하면 두 가지가 바뀌어요. 첫째, 가려져 있던 <<Paste>> 바인딩이 드러나서 Control-y가 더는 <Control-y> 바인딩을 호출하지 않고 대신 가상 이벤트 <<Paste>>를 호출해요. 둘째, F6 키를 눌러도 이제 <<Paste>> 바인딩을 호출해요.
마우스 포인터 이동시키기
마우스 포인터를 실제로 움직여야 할 때가 있어요. 예를 들어 사용자에게 프로그램 사용법을 직접 시연해 주는 소프트웨어가 있다면요. 그럴 땐 event generate로 마우스를 "warp"하면 돼요.
for {set xy 0} {$xy < 200} {incr xy} {
event generate . <Motion> -x $xy -y $xy -warp 1
update
after 50
}
보통 사용자의 마우스 포인터를 움직이는 건 통제권을 빼앗기 때문에 좋지 않은 스타일로 여겨져요. 그러니 주의해서 써야 하고, 모든 플랫폼에서 동작한다는 보장도 없어요.
더 알아보기
bind 문서를 함께 보세요. 가상 이벤트 바인딩과 퍼센트 치환의 자세한 동작이 거기 설명돼 있어요.