- 전체
- C/C++ 일반
- C/C++ 수학
- C/C++ 그래픽
- C/C++ 자료구조
- C/C++ 인공지능
- C/C++ 인터넷
- wxWidget
- GTK+
- UNIX or LINUX programming
- 리눅스 마스터 - 국가공인자격
- VC++/ MFC
- C#/CLI/.NET
- QT/기타UI
- Boost lib
- 오픈소스 C 분석자료
- MSA (마이크로서비스), Docker, kubernetes
- WSL(windows subsystem linux)
C/C++ 일반 C++ - Event-Driven Programming
2013.11.25 13:58
[출처] http://www.husseinsspace.com/teaching/udw/1996/cnotes/chapsix.htm
C++ - Event-Driven Programming
Introduction
In the classical approach to programming, events happen in a pre-defined sequence. In fact, classical programmers go the extent of defining programs as sets of instructions that are executed sequentially by the computer. Of course there are loop and jumps but these are still programmed sequentially. However, there are times when you want the program to respond to events on its own rather than check diligently for the event. A typical example is the incorporation of so-called "hotkeys" into a program. In the classical approach, the programmer has to constantly check for these keys, every time a key is pressed. This means a lot of extra effort for the programmer and it only gets worse as the complexity of the program increases.
An ideal situation would be one where the program responds of its own accord to such events like hotkeys. It can be done using input filters and other such innovations but if the program is object-oriented, conversion to event-driven programming is much easier.
Event-driven programming is where the program responds to events rather than follow a sequential course of instructions. Event-driven programming is especially useful in object-oriented environments and where graphical user interfaces are being constructed. Most high-level GUI APIs (OWL, MFC, Turbo Vision, etc) use event-driven programming to embed basic features into every program.
Principles of Event-driven Programming
Firstly, the computer is a sequential device and therefore cannot, on its own accord, respond to events. To simulate this auto-responding, a kernel must be constructed. This kernel must know about all objects in the program and must allow communication with these objects.
Events can be defined as any external influence on the computer. Keystrokes are events as are mouse movements and clicks. Events can also be explicitly sent from one object to another in the form of a message. These are useful for intra-process and inter-process communication. Idle events can also be generated automatically by the kernel to allow for background processing.
Lastly, a basic set of objects must be built, following strict guidelines in terms of interfacing with the kernel. Each top-level object must have the ability to respond to events.
Thus, instead of executing a sequential program, you simply execute the kernel. The kernel will then collect all events, including various forms of input, and pass it on to the relevant objects for processing.
GUIs
In a graphical user interface, the basic object is usually the window. Windows should therefore be capable of processing input. Thus they can process events. All descendants of the basic window object must also have the capability of processing events.
The kernel is normally part of the compiler's libraries (Turbo Vision) or the operating system (OWL/Microsoft Windows). This kernel provides the basis for all activity on the computer by gathering and dispatching events.
Event-driven programming lends itself to multi-tasking since the windows do not themselves have the input focus. The kernel always controls the program and can just as easily control two programs on the screen. Events can be filtered from one window, where they are not needed, to another where they are. Also, events can be buffered and processed when the kernel is idle. Since most graphical operating systems are multi-tasking, their programming APIs are invariably OO and event-driven (eg. OWL, MFC) so as to most accurately model the OS.
Windows as objects can either be self-regenerative or overlapped. MS-Windows uses the former technique where every window must be able to redraw itself at any time. Some older text-based GUIs used the latter technique of each window storing its background - the disadvantage of this is that you cannot change the window order - advantage is that its easier to program. Each window and other object must process events as fast as possible. If the event takes long to process, it must be shifted into an internal queue and processed in the background. This enables cooperative multi-tasking.
Events can be packages into objects where each type of event is identified by a code. Simple GUIs can use keystrokes to denote events.
The kernel can be run in the background or as the main process. If the kernel is part of the operating system, it is always stable and cannot be corrupted by incorrect interfacing on the part of the programmer. However, if the kernel is the main process for the program, all objects created must conform to the interface - this is essential for cooperative multi-tasking - if one object misbehaves all the others will fail to perform satisfactorily.
Sample Event-Driven GUI
This program creates a kernel in the object called Application. The basic Window object is kept in a linked list attached to the kernel. The kernel continuously searches for keystrokes and, if it finds any, passes these to the topmost window. The topmost window will try to process the keystroke, and if it fails will return this status to the kernel, which continues to traverse the linked list until it encounters success.
Window has two event-processing routines which are virtual, ProcessKey and IdleAction. The former processes keystrokes and the latter does background processing when no events are being created. Mouse events and inter-object events are not supported.
BWindow is derived from Window and enhances it by drawing a border around the window. It also shrinks the window by one block all around to exclude the border.
HorzMenu and VertMenu are basic horizontal and vertical menus that fit within a Window. They each take a list of menu items defined by a MenuData object. From these are derived the main menu and file menu of the program.
The StatusLine and MessageWindow are specialist derivation from BWindow, which display a scrolling status bar and a modal message box.
// event-driven GUI class definitions #include <conio.h> #include <alloc.h> #include <iostream.h> #include <string.h> #include <dos.h> #include <stdlib.h> #include "event.h" // ********************************************************************** // base class for all windows and window-elements // ********************************************************************** // create a new window Window::Window ( byte xx1, byte yy1, byte xx2, byte yy2 ) { x1=xx1; y1=yy1; x2=xx2; y2=yy2; // save coordinates struct text_info ti; // save information about old window gettextinfo (&ti); oldx1=ti.winleft; oldy1=ti.wintop; oldx2=ti.winright; oldy2=ti.winbottom; oldx=ti.curx; oldy=ti.cury; oldattr=ti.attribute; Store=(byte *)malloc(4000); // get memory and store background image gettext (x1, y1, x2, y2, Store); window (1, 1, 80, 25); // set up window window (x1, y1, x2, y2); textattr (7); } // destroy a window Window::~Window () { window (1, 1, 80, 25); // restore window coordinates window (oldx1, oldy1, oldx2, oldy2); puttext (x1, y1, x2, y2, Store); // restore background image free (Store); // free image memory textattr (oldattr); // restore cursor/colours gotoxy (oldx, oldy); } // placeholder for key handling routines in descendants int Window::ProcessKey ( char ) { // return values : // 1 = key was not used - pass to next window // 0 = key was used up // 2 = application has terminated return 1; }; // placeholder for idle processing in descendants void Window::IdleAction () { }; // ********************************************************************** // main kernel class // ********************************************************************** Application *anApp; // global variable to access kernel // initialize kernel with no windows Application::Application () { Head=NULL; anApp=this; } // kill kernel by destroying all windows Application::~Application () { while (Head!=NULL) CloseWindow (); } // attach a new window to the kernel void Application::OpenWindow ( Window *p ) { p->Next=Head; Head=p; } // remove a window from the kernel's list void Application::CloseWindow () { if (Head!=NULL) { Window *p=Head; Head=Head->Next; delete p; } } // main message-processing loop void Application::Run () { char ch; int Status=0; do { if (kbhit ()) // check for key pressed { ch=getch (); // get keystoke if (ch!='Q') { // pass it to all windows until it is Window *p=Head; // .. processed while ((p!=NULL) && ((Status=(p->ProcessKey (ch)))==1)) p=p->Next; } } else // otherwise, do idle processing { Window *p=Head; // .. for all windows while (p!=NULL) { p->IdleAction (); p=p->Next; } } } while (Status!=2); // until a windows returns exit state } // ********************************************************************** // window with a border // ********************************************************************** BWindow::BWindow ( byte xx1, byte yy1, byte xx2, byte yy2, byte block ) : Window ( xx1, yy1, xx2, yy2 ) { if (block==1) Block (); } // output a character directly to the video memory void BWindow::directput ( int x, int y, byte cha, byte col ) { unsigned int xo=x; unsigned int yo=y; unsigned int offset=(yo-1)*160+(xo-1)*2; pokeb (0xB800, offset, cha); pokeb (0xB800, offset+1, col); } // make a block around the current window and shrink it void BWindow::Block () { int a; directput (x1, y1, 218, 7); directput (x2, y1, 191, 7); directput (x1, y2, 192, 7); directput (x2, y2, 217, 7); for ( a=1; a<x2-x1; a++) { directput (x1+a, y1, '?, 7); directput (x1+a, y2, '?, 7); } for ( a=1; a<y2-y1; a++) { directput (x1, y1+a, '?, 7); directput (x2, y1+a, '?, 7); } window (x1+1, y1+1, x2-1, y2-1); clrscr (); } // ********************************************************************** // window with a single-line message // ********************************************************************** // create window with border and display message MessageWindow::MessageWindow ( char *s ) : BWindow (5, 10, 75, 14, 1) { int start=(68-strlen (s))/2; for ( int a=0; a<start; a++ ) putch (' '); for ( a=0; a<strlen (s); a++ ) putch (s[a]); gotoxy (22, 3); cout << "press any key to continue"; } // close window when user presses any key int MessageWindow::ProcessKey ( char ) { anApp->CloseWindow (); return 0; } // ********************************************************************** // menu item // ********************************************************************** // ********************************************************************** // linked list of menu items // ********************************************************************** // destroy menu item list MenuData::~MenuData () { while (Head!=NULL) { MenuItem *p=Head; Head=Head->Next; delete p; } } // ********************************************************************** // horizontal menu // ********************************************************************** HorzMenu::HorzMenu ( byte xx1, byte yy1, byte xx2, byte yy2, byte block ) : BWindow ( xx1, yy1, xx2, yy2, block ) { } // initialize menu and draw items on screen void HorzMenu::InitMenu ( MenuData *md ) { Position=0; Menu=md; MenuItem *p=Menu->Head; // find maximum length of item int MaxLength=0; while (p!=NULL) { if (strlen (p->Data)>MaxLength) MaxLength=strlen (p->Data); p=p->Next; } Separator=MaxLength+2; // set separator distance for ( int a=0; a<Menu->Count; a++ ) // write items Draw (a, 0); Draw (Position, 1); // highlight first item } // write or highlight an item void HorzMenu::Draw ( int Pos, int State ) { MenuItem *m=Menu->Head; // search linked list for item int a=Pos; while (a>0) { a--; m=m->Next; } gotoxy (Pos*Separator+1, 1); // set cursor position & colour if (State==0) textattr (7); else textattr (112); for ( a=0; a<strlen (m->Data); a++ ) // output string putch (m->Data[a]); } // process left and right arrows int HorzMenu::ProcessKey ( char ch ) { switch (ch) { case 75 : if (Position>0) // left arrow { Draw (Position, 0); // move left Position--; Draw (Position, 1); }; return 0; case 77 : if (Position < (Menu->Count-1)) // right arrow { Draw (Position, 0); // move right Position++; Draw (Position, 1); } return 0; default : return 1; // pass keystroke to next window } } // ********************************************************************** // vertical menu // ********************************************************************** VertMenu::VertMenu ( byte xx1, byte yy1, byte xx2, byte yy2, byte block ) : BWindow ( xx1, yy1, xx2, yy2, block ) { } // initialise menu and draw items on screen void VertMenu::InitMenu ( MenuData *md ) { Position=0; Menu=md; for ( int a=0; a<Menu->Count; a++ ) // write items Draw (a, 0); Draw (Position, 1); // highlight first item } // write or highlight an item void VertMenu::Draw ( int Pos, int State ) { MenuItem *m=Menu->Head; // search linked list for item int a=Pos; while (a>0) { a--; m=m->Next; } gotoxy (1, Pos+1); // move cursor and set colour if (State==0) textattr (7); else textattr (112); for ( a=0; a<strlen (m->Data); a++ ) // output string putch (m->Data[a]); } // process up and down arrows int VertMenu::ProcessKey ( char ch ) { switch (ch) { case 72 : if (Position>0) // up arrow { Draw (Position, 0); // move up Position--; Draw (Position, 1); }; return 0; case 80 : if (Position < (Menu->Count-1)) // down arrow { Draw (Position, 0); // move down Position++; Draw (Position, 1); } return 0; case 27 : anApp->CloseWindow (); // ESC = close window return 0; default : return 1; // pass keystroke to next window } } // ********************************************************************** // scrolling status line // ********************************************************************** StatusLine::StatusLine ( byte y, char *s ) : BWindow (1, y, 80, y, 0) { strcpy (Data, s); while (strlen (Data)<80) // pad line with spaces on left/right { strcat (Data, " "); if (strlen (Data)<80) { char t[100]=" "; strcat (t, Data); strcpy (Data, t); } } waitcount=0; IdleAction (); // display initial string } // display string and move it one space to the left void StatusLine::IdleAction () { waitcount++; // divide-by-N counter to slow down if (waitcount!=600) // .. iterations return; waitcount=0; for ( int a=0; a<strlen (Data); a++ ) // display string directput (a+1, y1, Data[a], 112); char ch=Data[0]; // rotate string left for ( a=0; a<strlen (Data)-1; a++ ) Data[a]=Data[a+1]; Data[strlen (Data)-1]=ch; } // ********************************************************************** |
kernel and basic objects header file
// event-driven GUI header // define often-used data types typedef unsigned char byte; // ********************************************************************** // base class for all windows and window-elements // ********************************************************************** class Window { public: byte *Store; // storage for background image byte x1, y1, x2, y2; // windows coordinates byte oldx1, oldy1, oldx2, oldy2; // old window coordinates byte oldx, oldy, oldattr; // old cursor position/colours Window *Next; // ptr to window underneath Window ( byte xx1, byte yy1, byte xx2, byte yy2 ); ~Window (); virtual int ProcessKey ( char ); virtual void IdleAction (); }; // ********************************************************************** // main kernel class // ********************************************************************** class Application { public: Window *Head; // ptr to topmost window Application (); ~Application (); void OpenWindow ( Window *p ); void CloseWindow (); void Run (); }; extern Application *anApp; // global variable to access kernel // ********************************************************************** // window with a border // ********************************************************************** class BWindow : public Window { public: BWindow ( byte xx1, byte yy1, byte xx2, byte yy2, byte block ); void directput ( int x, int y, byte cha, byte col ); void Block (); }; // ********************************************************************** // window with a single-line message // ********************************************************************** class MessageWindow : public BWindow { public: MessageWindow ( char *s ); virtual int ProcessKey ( char ); }; // ********************************************************************** // menu item // ********************************************************************** class MenuItem { public: char Data[100]; // actual data for menu MenuItem *Next; // pointer to next item MenuItem ( char *p ) { strcpy (Data, p); Next=NULL; } }; // ********************************************************************** // linked list of menu items // ********************************************************************** class MenuData { public: MenuItem *Head, *Tail; // linked list pointers unsigned int Count; // number of items in list MenuData () // initialize list { Head=NULL; Tail=NULL; Count=0; }; void AddItem ( MenuItem *m ) // add an item to the list { if (Head==NULL) Head=m; else Tail->Next=m; Tail=m; Count++; } ~MenuData (); }; // ********************************************************************** // horizontal menu // ********************************************************************** class HorzMenu : public BWindow { public: MenuData *Menu; // pointer to a list of menu items byte Separator; // distance between menu items byte Position; // current position of selector block HorzMenu ( byte xx1, byte yy1, byte xx2, byte yy2, byte block ); void InitMenu ( MenuData *md ); void Draw ( int Pos, int State ); virtual int ProcessKey ( char ch ); }; // ********************************************************************** // vertical menu // ********************************************************************** class VertMenu : public BWindow { public: MenuData *Menu; // pointer to a list of menu items byte Position; // current position of selector block VertMenu ( byte xx1, byte yy1, byte xx2, byte yy2, byte block ); void InitMenu ( MenuData *md ); void Draw ( int Pos, int State ); virtual int ProcessKey ( char ch ); }; // ********************************************************************** // scrolling status line // ********************************************************************** class StatusLine : public BWindow { public: char Data[100]; // string for status line int waitcount; // delay in-between moves StatusLine ( byte y, char *s ); virtual void IdleAction (); }; // ********************************************************************** |
// sample program to illustrate an event-driven GUI #include <string.h> #include "event.h" // ********************************************************************** // file menu // ********************************************************************** class FileMenu : public VertMenu { public: MenuData *md; // menu item linked list FileMenu (); ~FileMenu () { delete md; }; virtual int ProcessKey ( char ch ); }; // set menu items and display menu FileMenu::FileMenu () : VertMenu (3, 3, 12, 7, 1) { md=new MenuData; md->AddItem (new MenuItem ("Open")); md->AddItem (new MenuItem ("-------")); md->AddItem (new MenuItem ("Exit")); InitMenu (md); } // process arrows and hotkeys int FileMenu::ProcessKey ( char ch ) { switch (ch) { case 'e' : case 'E' : return 2; case 'O' : case 'o' : anApp->OpenWindow (new MessageWindow ("Not yet implemented")); return 0; case 13 : switch (Position) // process ENTER { case 0 : anApp->OpenWindow (new MessageWindow ("Not yet implemented")); return 0; case 2 : return 2; default : return 0; } default : VertMenu::ProcessKey (ch); // process arrows return 0; } } // ********************************************************************** // main menu // ********************************************************************** class TopMenu : public HorzMenu { public: MenuData *md; // menu item linked list TopMenu (); ~TopMenu () { delete md; }; virtual int ProcessKey ( char ch ); }; // set menu item and display menu TopMenu::TopMenu () : HorzMenu (1, 1, 80, 3, 1) { md=new MenuData; md->AddItem (new MenuItem ("File")); md->AddItem (new MenuItem ("Help")); md->AddItem (new MenuItem ("About")); InitMenu (md); }; // process arrows and hotkeys int TopMenu::ProcessKey ( char ch ) { switch (ch) { case 'f' : case 'F' : anApp->OpenWindow (new FileMenu); return 0; case 'h' : case 'H' : anApp->OpenWindow (new MessageWindow ("no HELP available")); return 0; case 'a' : case 'A' : anApp->OpenWindow (new MessageWindow ("Windows Demonstration program")); return 0; case 13 : switch (Position) // process ENTER { case 0 : anApp->OpenWindow (new FileMenu); return 0; case 1 : anApp->OpenWindow (new MessageWindow ("no HELP available")); return 0; default : anApp->OpenWindow (new MessageWindow ("Windows Demonstration program")); return 0; } default : HorzMenu::ProcessKey (ch); // process arrows return 0; } } // ********************************************************************** // main program body // ********************************************************************** void main () { // create application kernel Application MyApp; // make initial windows MyApp.OpenWindow (new BWindow (1, 4, 80, 24, 1)); MyApp.OpenWindow (new StatusLine (25, "Window Demonstration :: Event-driven programming :: H. Suleman :: 1996")); MyApp.OpenWindow (new TopMenu); // start main event-processing loop MyApp.Run (); } |
광고 클릭에서 발생하는 수익금은 모두 웹사이트 서버의 유지 및 관리, 그리고 기술 콘텐츠 향상을 위해 쓰여집니다.
댓글 0
| 번호 | 제목 | 글쓴이 | 날짜 | 조회 수 |
|---|---|---|---|---|
| 16 |
[C/C++ 자료구조] 아스키 코드표(ASCII Table)
| 졸리운_곰 | 2023.08.16 | 360 |
| 15 | [C 프로그래밍] 파일 출력 함수_3.연결 리스트 저장, 불러오기 | 졸리운_곰 | 2022.06.17 | 322 |
| 14 |
[C/C++ 자료구조] 5일만에 뚝딱 스크립트 언어 만들기 PGLight (1/5)
| 졸리운_곰 | 2021.04.12 | 301 |
| 13 |
[C/C++] 미니멀 가상화폐 코드 , 미니멀 블룩체인, noincoin (Block Chain in C Major)
| 졸리운_곰 | 2021.01.30 | 279 |
| 12 |
Visual Studio Community로 V8 엔진 다운로드 및 빌드하고 간단히 살펴보기
| 졸리운_곰 | 2020.11.30 | 271 |
| 11 | zlib msvc로 포팅하기 2 | 졸리운_곰 | 2018.02.23 | 356 |
| 10 | zlib msvc로 포팅하기 1 | 졸리운_곰 | 2018.02.23 | 288 |
| 9 | zlib 개요 | 졸리운_곰 | 2018.02.23 | 572 |
| 8 | [zlib] API 사용법 | 졸리운_곰 | 2018.02.23 | 502 |
| 7 | [zlib] DEFLATE algorithm (2) - Huffman coding | 졸리운_곰 | 2018.02.23 | 352 |
| 6 | [zlib] DEFLATE algorithm (1) - LZ77 | 졸리운_곰 | 2018.02.23 | 504 |
| 5 |
zlib 입문
| 졸리운_곰 | 2018.02.23 | 629 |
| 4 | [펌] zlib를 사용법 | 졸리운_곰 | 2018.02.23 | 783 |
| 3 |
Create Custom Binary File Formats for Your Game's Data
| 졸리운_곰 | 2018.02.23 | 373 |
| 2 |
GitHub - codelibs/libdxfrw: C++ library to read and write DXF/DWG files
| 졸리운_곰 | 2017.04.27 | 655 |
| 1 | C++ Json Parser 조사 | 졸리운_곰 | 2015.11.10 | 1004 |

