[출처] 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.



kernel and basic objects


경축! 아무것도 안하여 에스천사게임즈가 새로운 모습으로 재오픈 하였습니다.
어린이용이며, 설치가 필요없는 브라우저 게임입니다.
https://s1004games.com

event.cpp

// 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.h

 
// 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 ();
};

// **********************************************************************


main program



eventtest.cpp

// 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 ();
}







본 웹사이트는 광고를 포함하고 있습니다.
광고 클릭에서 발생하는 수익금은 모두 웹사이트 서버의 유지 및 관리, 그리고 기술 콘텐츠 향상을 위해 쓰여집니다.
번호 제목 글쓴이 날짜 조회 수
20 [visual c++] 오류 D8016 '/ZI'과(와) '/Gy-' 명령줄 옵션이 호환되지 않습니다. file 졸리운_곰 2023.11.19 322
19 [visual studio] Bring Your MFC Application to the Web mfc 어플리케인 web 구동 file 졸리운_곰 2023.03.20 279
18 [visual studio] [borland c] Using the WinBGIm Graphics Library with Visual Studio 2005/2008 2010 file 졸리운_곰 2023.02.05 315
17 [Visual Studio][boost c++] [C++] boost 설치 및 visual studio 설정 file 졸리운_곰 2021.11.18 464
16 winbgi Borland Turbo C routines in MS Visual C++ file 졸리운_곰 2017.03.04 290
15 VS 프로젝트 이름 변경 졸리운_곰 2017.02.12 355
14 Retrieving CPU Load Percent total in Windows with C++ 졸리운_곰 2016.12.31 487
13 Cpu 사용율 알아내는 소스 졸리운_곰 2016.12.31 471
12 [MFC] Find local network hosts ip, 로컬네트워크의 IP 주소 알아내기 졸리운_곰 2014.08.27 766
11 (Unicode) CString <==> const char* 졸리운_곰 2014.08.27 640
10 Creating a Child Process with Redirected Input and Output 졸리운_곰 2014.08.17 573
9 C++로 짠 간단한 윈도우 서비스 file 졸리운_곰 2014.05.27 1003
8 VC++ 파일에서 한 라인씩 읽기 졸리운_곰 2014.03.02 1199
7 Search Title Of Opened Windows file 졸리운_곰 2014.02.27 978
6 MFC VC++ 파일의 존재유무 체크 졸리운_곰 2014.02.18 1587
5 MFC VC++ : windows tcpip socket Send() function example 졸리운_곰 2014.02.18 1593
4 Check whether one specific process is running on windows with C++ 졸리운_곰 2014.02.18 1099
3 VC++ : Win32 console App run as to be hide (background) 졸리운_곰 2014.02.18 1245
2 VC++ : Win32 console App run as to be hide (background) 졸리운_곰 2014.02.18 1256
1 VC++ : windows : TCPIP SOCKET SERVER and CLIENT 졸리운_곰 2014.02.18 1097
대표 김성준 주소 : 경기 용인 분당수지 U타워 등록번호 : 142-07-27414
통신판매업 신고 : 제2012-용인수지-0185호 출판업 신고 : 수지구청 제 123호 개인정보보호최고책임자 : 김성준 sjkim70@stechstar.com
대표전화 : 010-4589-2193 [fax] 02-6280-1294 COPYRIGHT(C) stechstar.com ALL RIGHTS RESERVED