Creating User Interface Thread Part II

Download source code here  User Interface Thread Part II

We know that unlike a worker thread, a user-interface (UI) thread is used to create windows and process messages sent to those windows. In this article we will write a small application that uses a UI thread. This application would read a file containing information about lines, rectangles and ellipses. In addition to coordinates of these shapes, this information comprises line color and line thickness. Using this information the application draws these objects in a view window. If the file contains a lot of data then unless one file has been loaded and displayed we cannot load another file. This situation is ideal for implementing the reading and displaying in a user-interface thread. Carry out the following steps to create this application.

Create an SDI application. Edit the menu created by the AppWizard so that only ‘File’ menu having one menu item ‘New’ remains. Change the ID of ‘New’ to ID_NEW.

Create a dialog box having an edit box and a ‘Browse’ button having IDs IDC_FNAME and IDC_BROWSE respectively. Insert a new class filedialog for this dialog. Add OnInitDialog( ) handler to the filedialog class. Add the following statements to OnInitDialog( ) function.

BOOL filedialog::OnInitDialog( )

{

m_fname = “Enter File Name” ;

CDialog::OnInitDialog( ) ;

return TRUE ;

}

This ensures that when the dialog is displayed the edit box displays the string “Enter File Name”.

Add OnBrowse( ) handler for the ‘Browse’ button. This function is given below:

void filedialog::OnBrowse( )

{

CFileDialog fd ( 1, 0, 0, 0, “Object Files(*.lre)|*.lre|All Files|*.*||” ) ;

fd.m_ofn.lpstrTitle = “Browse” ;

if ( fd.DoModal( ) == IDOK )

m_fname = fd.GetPathName( ) ;

objfilename = m_fname ;

UpdateData ( FALSE ) ;

}

This function displays the standard ‘File’ dialog box that allows user to select the data file (one with .lre extension). Once the file is selected we have retrieved its name using CFileDialog::GetPathName( ) and then stored it in a global variable objfilename. Declare this global variable in ‘filedialog.cpp’ as a CString object. Calling UpdateData ( FALSE ) displays the selected file in the edit box.

Add OnOK( ) handler to the filedialog class. This function is given below:

void filedialog::OnOK( )

{

CDialog::OnOK( );

if ( ( m_fname == “” ) || ( m_fname == “Enter File Name” ) || ( m_fname.Right ( 4 ) != “.lre” ) )

{

MessageBox ( “Invalid File Format” ) ;

return ;

}

objfilename = m_fname ;

AfxBeginThread ( RUNTIME_CLASS ( UIthread ) ) ;

}

In this function we have verified that the filename entered by the user is a valid filename. The filename must have an extension ‘.lre’. Next, we have initiated a UI thread using the AfxBeginThread( ) function. To this function we have passed CRuntimeClass structure of the thread class UIthread. Whenever the user selects a ‘.lrc’ file from the dialog a new window should be popped up through the UI thread. Since we do not know how many threads the user will start the objects of this class must be created dynamically.

Using ClassWizard add a new class called UIthread derived from CWinThread. Add following code to it.

class UIthread : public CWinThread

{

DECLARE_DYNCREATE ( UIthread )

public :

BOOL InitInstance( ) ;

} ;

Add the following code to the class’s implementation file.

#include “myframe.h”

IMPLEMENT_DYNCREATE ( UIthread, CWinThread )

BOOL UIthread::InitInstance( )

{

myframe *p = new myframe ;

m_pMainWnd = p ;

p -> ShowWindow ( 1 ) ;

return TRUE ;

}

In InitInstance( ) function the frame window is created. myframe class is derived from CFrameWnd class. Insert this class by selecting ‘Insert | New Class’. Type the class name as myframe and select CFrameWnd class as the base class. Two files, ‘myframe.h’ and ‘myframe.cpp’ would get added to the project.

In ‘myframe.h’ make the declaration of the myframe( ) constructor public (by default it is protected) as we have to access it in UIthread class). Add a statement in the constructor as shown below:

myframe::myframe( )

{

Create ( 0, “Draw Objects” ) ;

}

Now add a variable m_view of type objectview* to the myframe class. objectview is a user-defined class derived from CView class. Make the declaration of myframe( ) constructor public. #include ‘objectview.h’ in ‘myframe.h’ file.

Add OnCreate( ) handler to the myframe class and add the following code to it.

int myframe::OnCreate ( LPCREATESTRUCT lpCreateStruct )

{

if ( CFrameWnd::OnCreate ( lpCreateStruct ) == -1 )

return -1;

m_view = new objectview ;

m_view -> Create ( 0, “”, WS_CHILD | WS_VISIBLE, rectDefault, this, AFX_IDW_PANE_FIRST ) ;

return 0;

}

Here, we are creating the view window dynamically.

Add the objectview class by selecting ‘Insert | New Class’. Give the class name as ‘objectview’ and select CView as the base class. The two files ‘objectview.h’ and ‘objectview.cpp’ would get added to the project. Make the objectview constructor public so that we can access it in myframe class.

In the constructor assign the filename stored in global variable to m_filename that is the private data member (of type CString) of objectview class.

objectview::objectview( )

{

m_filename = objfilename ;

}

Add the following code to the OnDraw( ) function.

void objectview::OnDraw ( CDC* pDC )

{

CDocument* pDoc = GetDocument( ) ;

char objtype ;

int left, top, right, bottom, thick ;

int red, green, blue ;

CPen mypen, *oldpen ;

CBrush mybrush ;

mybrush.CreateStockObject ( NULL_BRUSH ) ;

pDC -> SelectObject ( &mybrush ) ;

CStdioFile f ;

if ( f.Open ( m_filename, CFile::modeRead ) == 0 )

{

MessageBox ( “File Not Found” ) ;

return ;

}

CString str ;

while ( f.ReadString ( str ) != NULL )

{

sscanf ( str, “%c %d %d %d %d %d %d %d %d”, &objtype, &left,

&top, &right, &bottom, &red, &green, &blue, &thick ) ;

if ( objtype != ‘L’ && objtype != ‘R’ && objtype != ‘E’ )

continue ;

if ( mypen.m_hObject != NULL )

mypen.DeleteObject( ) ;

mypen.CreatePen ( PS_SOLID, thick, RGB ( red, green, blue ) ) ;

oldpen = pDC -> SelectObject ( &mypen ) ;

switch ( objtype )

{

case ‘L’ :

pDC -> MoveTo ( left, top ) ;

pDC -> LineTo ( right, bottom ) ;

break ;

case ‘R’ :

pDC -> Rectangle ( left, top, right, bottom ) ;

break ;

case ‘E’ :

pDC -> Ellipse ( left, top, right, bottom ) ;

break ;

}

}

f.Close( ) ;

pDC -> SelectObject ( oldpen ) ;

}

Firstly, declare the variables in which information retrieved from the data file will be stored. The data file must store the information of objects in the following format:

// obj1.lre

L 10 10 200 200 255 255 0 2

R 50 10 200 10 100 100 100 3

E 200 200 300 250 255 0 0 4

The first character specifies the type of the object i.e., line, rectangle or ellipse. Next four entries are the coordinates of the object. Next three entries specify the RGB elements of the pen color and the last entry is the thickness of the pen.

Using CStdioFile::Open( ) function the filename stored in m_filename is opened. The records are read from the file by calling CStdioFile::ReadString( ) function. The different entries in the record stored in str are then assigned to the variables using sscanf( ) function. Before drawing an object a pen of appropriate color and thickness is created. A NULL brush too is created so that all the objects are visible even if they get overlapped. Lastly, according to the type of object it is drawn in the view window using member functions of CDC class.

Download source code here  User Interface Thread Part II

This entry was posted in MFC and tagged . Bookmark the permalink.

Leave a Reply

Your email address will not be published. Required fields are marked *

*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>