Archive for the ‘GUI’ category

Cross-platform Development – Qt

March 20th, 2010

Cross platform development has been a goal that I have held very close to my heart for quite some time. The notion of having an application or even a simple utility that can work across Windows, Linux and Mac somehow presents a charm that I find very appealing and something, try as I may, that keeps haunting my thoughts.

I tried my bit on wxWidgets, but found its MFC style architecture a little boring. I’m sure it works very well and there are many applications that use wxWidgets, but somehow wanted a framework that’s commercially supported while being a little bit more pure C++. (wxWidgets3 is supposed to be this, but it’s been coming for a very loong time).

I have even dabbled with XUL and XPCOM, but found the lack of a proper development tool very stifling. It’s amazing how many open source projects that have really cool technologies do not get their fair attention from other developers because of lack of proper tools. If only some of the Firefox team members forked out a project to create an integrated IDE that allowed designing the GUI and adding various event handlers from it. The whole concept of developing front end using an interpreted language such as Javascript is wonderful as it allows you to make quick changes to the presentation layer and get visual feedback immediately. In fact, this is better than the statically bound GUI such as those delivered by MFC, wxWidgets and such, but one needs a proper tool to create the initial content.

Anyway, having given up on the above two, I recently revisited another candidate that I had evaluated earlier — Qt. I had given up on Qt few years ago for their licensing costs were far too high. I felt that for a company trying to sell a new application framework, to get one better over the incumbents, they ought to price is really low. Anyway, with the recent acquisition by Nokia and their decision to have a GPL version that allows full proprietorship for applications built using it, I felt I ought to give it another shot. Also, their new tool, Qt Creator — the core IDE that allows everything to be done from one application — sounds very promising. Let’s hope it lives up to its perceived (purely mine) promise!

So over the next few weeks (or even months) I plan to spend a little bit of  time every week to look into Qt and how to create applications using it. I’ll try to document as much as I discover — both as a notes for my research as well as in the hope that someone else on the Net might find something useful in it. I can’t think of a specific application that I want to create, but I’m hoping that as I wander through the framework, I’ll have a ‘light-bulb’ moment leading to a worthy application to test the framework’s mettle.

Putting a child window inside a simple window

September 30th, 2008

This is a short write-up that I did quite a while ago to show to a newly joined colleague how MFC UI can be created from the program without requiring the definition of resources. This is really very simple MFC programming and I’m including it here as I’m going to shut down the other blog site that I used to maintain and want to transfer some of the content that I spent some effort on to here.

This sample shows how a child window can be embedded inside the simple window that we created in the previous lesson. Objectives of this write-up are:

  • Show how windows messages can be trapped and handled in window-object methods.
  • Creation of a standard control inside a parent window.
  • Proper handling of WM_SIZE message so that the child window occupies the entire client-area of the parent window when parent window is resized.
  • Retrieving the handle to the child window from outside the scope of the parent window in order to change its properties.

Code

#include <afxwin.h>
#include <afxcmn.h>

// declare a simple window
class MyWnd : public CWnd {
	//
	// child window is a list-view control, but can be
	// substituted with any other standard windows control
	CListCtrl m_wndList;

public:
	static const UINT IDW_LISTCTRL = 1000;

	MyWnd() : CWnd()
	{}

	DECLARE_MESSAGE_MAP()

	int OnCreate(LPCREATESTRUCT lpCreateStruct)
	{
		// pass the message to base class so that it can complete
		// its logic
		if (CWnd::OnCreate(lpCreateStruct) == -1)
			return -1;  // -1 indicates OnCreate failure
		// create the list-view control
		CRect rc;
		if (!m_wndList.CreateEx(
			WS_EX_CLIENTEDGE, // window extended style
			WS_VISIBLE|WS_CHILD|
			WS_CLIPCHILDREN|LVS_REPORT, // window style
			rc, // unitialized rc is okay as we resize the child
			    // window in the WM_SIZE handler
			this, // parent window
			IDW_LISTCTRL)) // window id
			return -1;
		//
		// add a few columns to the list-view control.
		m_wndList.InsertColumn(
			0, // column index
			_T("Name"), // title
			LVCFMT_LEFT, // column content format style
			150, // column width
			0); // subitem index that uniquely refers to this column
		m_wndList.InsertColumn(1,
			_T("Address1"),
			LVCFMT_LEFT,
			200,
			1);
		m_wndList.InsertColumn(2,
			_T("Address2"),
			LVCFMT_LEFT,
			200,
			2);
		m_wndList.InsertColumn(3,
			_T("Country"),
			LVCFMT_LEFT,
			80,
			3);
		m_wndList.InsertColumn(4,
			_T("Zip"),
			LVCFMT_LEFT,
			60,
			4);
		return 0;
	}
	void OnSize(UINT uSizeType, int cx, int cy)
	{
		CWnd::OnSize(uSizeType, cx, cy);
		// resize the child window to fit the entire client area
		m_wndList.MoveWindow(0, 0, cx, cy);
	}
};

BEGIN_MESSAGE_MAP(MyWnd, CWnd)
	ON_WM_CREATE()
	ON_WM_SIZE()
END_MESSAGE_MAP()

//
// There must be one instance of a CWinApp derived class per program
class MyApp : public CWinApp {
	CWnd* m_pWnd;
public:
	MyApp()
	: CWinApp()
	, m_pWnd(0)
	{}
	BOOL InitInstance()
	{
		MyWnd* pWnd = new MyWnd;
		if (!pWnd->CreateEx(WS_EX_CLIENTEDGE,
			::AfxRegisterWndClass(0, 0, ::GetSysColorBrush(COLOR_WINDOW), 0),
			_T("My Window"),
			WS_OVERLAPPEDWINDOW,
			0,
			0,
			CW_USEDEFAULT,
			CW_USEDEFAULT,
			NULL,
			0) )
			return FALSE;
		m_pWnd = m_pMainWnd = pWnd;

		// retrieve a pointer to the list-view child window and append an item to it.
		CListCtrl* pListCtrl = (CListCtrl*)m_pWnd->GetDlgItem(MyWnd::IDW_LISTCTRL);
		ASSERT(pListCtrl);
		int iItem = pListCtrl->InsertItem(
			pListCtrl->GetItemCount(),
			_T("John Smith"));
		pListCtrl->SetItemText(iItem, 1, _T("110 Sunset Blvd"));
		pListCtrl->SetItemText(iItem, 2, _T("Ocean Drive"));
		pListCtrl->SetItemText(iItem, 3, _T("Singapore"));
		pListCtrl->SetItemText(iItem, 4, _T("234590"));

		// add an extended list-view specific style to change the control's behavior
		// Specifically, we tell the control to highlight the entire item when it is
		// selected. By default only the coulmn 1 is highlighted when the item
		// is selected.
		pListCtrl->SetExtendedStyle(LVS_EX_FULLROWSELECT);

		pWnd->ShowWindow(SW_SHOW);
		pWnd->UpdateWindow();

		return TRUE;
	}
	int ExitInstance()
	{
		delete m_pWnd; m_pWnd = 0;
		return 0;
	}
};
MyApp theApp;

How to Compile

Compile the code using the command line:

cl /MT /EHsc /D "_UNICODE" /D "UNICODE" tut1.cpp /link 
/subsystem:windows /ENTRY:"wWinMainCRTStartup"

Discussion

In this sample, we add a child window inside the simple window that we created earlier. For simplicity, we use a standard windows control as the child window and I picked the list-view control as this is one of the most powerful, versatile and often misunderstood control of all windows controls.

Firstly we add two message handler methods to our MyWnd class. These methods handle WM_CREATE and WM_SIZE windows messages respectively. The prototypes for these methods are defined in CWnd base class (from which CListCtrl, the list-view control class, is derived) and all we do is override the respective virtual methods (all message handler methods are defined as virtual allowing the derived class to override them). In order to trap the WM_CREATE and WM_SIZE messages sent to MyWnd into our own methods we place the necessary macros ON_WM_CREATE() and ON_WM_SIZE() between the BEGIN_MESSAGE_MAP() and END_MESSAGE_MAP() macros. Though it might appear complicated, these macros do very little other than initialize the data structure declared through the DECLARE_MESSAGE_MAP() macro with the function pointers to the OnCreate and OnSize methods.

In OnCreate(), we first pass the call to the base class so that it can complete its handling. This is an approach that is used throughout MFC (save for a few special messages where our handler is executed first and the base class method is optionally invoked). Subsequently, the child window is created and in our case we add a few columns to the list-view control to illustrate how easy it is to add columns to the list-view control. Also, note how the list-view window styles are appended to the standard window styles to initialize the list-view control in report mode (LVS_REPORT window style). Take note of the last parameter to the CreateEx() call. This is the id of the list control window that uniquely identifies this window to MyWnd amongst all its children. We’ll use this later to retrieve a pointer to this window from outside the scope of MyWnd object and manipulate it.

In OnSize(), since we want the list-view control to occupy the entire client area, we resize the child control using the MoveWindow() method passing the client area dimensions cx and cy as parameter.

Lastly, inside MyApp::InitInstance() we access the list-view child window and manipulate it directly. This is done by retrieving a pointer to the child window object through the CWnd::GetDlgItem() method, which returns a CWnd* to a child window, given the child window’s id. Of course for our example, we could have made CListCtrl a public data member which would have given us direct access to the child window. However, the purpose of this sample is to demonstrate this concept which is used extensively throughout MFC programs, especially in dialogs, to work on child windows.

Exercise

Add another child window to MyWnd such that the two children, the original list-view control and the new child window occupy half the client area of MyWnd at all times.

Simple MFC Window – from commandline

September 25th, 2008
This program does pretty much nothing other than create a window. The
objectives are:
Demonstrate how simple it is to create a window using MFC classes
without using the wizard Demonstrate that compiling and linking an MFC
program is as simple as a regular console program Outline basic MFC
concepts
#include <afxwin.h>
// declare a simple window
class MyWnd : public CWnd {
public:
MyWnd() : CWnd()
{}
DECLARE_MESSAGE_MAP()
};
BEGIN_MESSAGE_MAP(MyWnd, CWnd)
END_MESSAGE_MAP()
//
// There must be one instance of a CWinApp derived class per program
class MyApp : public CWinApp {
CWnd* m_pWnd;
public:
MyApp()
: CWinApp()
, m_pWnd(0)
{}
BOOL InitInstance()
{
MyWnd* pWnd = new MyWnd;
if (!pWnd->CreateEx(WS_EX_CLIENTEDGE,
::AfxRegisterWndClass(0, 0,
::GetSysColorBrush(COLOR_WINDOW), 0),
_T(“My Window”),
WS_OVERLAPPEDWINDOW,
0,
0,
CW_USEDEFAULT,
CW_USEDEFAULT,
NULL,
0) )
return FALSE;
m_pWnd = m_pMainWnd = pWnd;
pWnd->ShowWindow(SW_SHOW);
pWnd->UpdateWindow();
return TRUE;
}
int ExitInstance()
{
delete m_pWnd; m_pWnd = 0;
return 0;
}
};
MyApp theApp;
Compile this using the following command line in Visual Studio 2005
Command Prompt:
cl /MT /EHsc /D “_UNICODE” /D “UNICODE” tut1.cpp /link
/subsystem:windows /ENTRY:”wWinMainCRTStartup”
MyWnd derives from CWnd which is the base MFC class that represents a
window. All windowing classes are derived from CWnd including the
standard Win32 controls such as Button, Listbox, etc. Right after its
constructor, MyWnd declares a message map using the
DECLARE_MESSAGE_MAP() macro. This macro expands to insert the necessary
data members in the class declaration that would allow us to specify the
class methods to be invoked for handling the various messages that are
sent to the window. Specifying the individual method to be invoked in
response to a windows message will be covered in the next tutorial.
Every MFC program requires a single instance of a CWinApp derived class.
This forms the entry point into the program upon its startup.
InitInstance() is this entry point and ExitInstance() being the exit
point that is invoked when the user quits the program. This would be a
good place to program specific initialization/de-initialization code.
Inside InitInstance(), we create the window by instantiating MyWnd
object and calling the CWnd::CreateEx() method. The second parameter to
CreateEx() is the return value from the call
::AfxRegisterWndClass(0, 0, ::GetSysColorBrush(COLOR_WINDOW), 0)
which essentialy returns an ATOM (a Win32 type) which specifies the
default properties for a window (such as default style, cursor,
background color, etc). Refer to the documentation of Win32 API
RegisterClass() for more on window classes.
Exercise
Change the background color of the window by changing the solitary
argument to GetSysColorBrush(). Refer to GetSysColor() documentation for
other valid values.
Change the window cursor from arrow to another cursor. That is, whenever
the mouse moves over the window, its cursor should change to the one
defined. Hint: Refer to the documentation of AfxRegisterWndClass().
The command line parameters /subsystem:windows /ENTRY:”wWinMainCRTStartup”
are passed to the linker. Investigate what these parameters do.

This is an article that I wrote sometime ago to demonstrate that MFC programs can be written and compiled much like the famed “Hello World” console example. This program does pretty much nothing other than create a window. The objectives are:

  • Demonstrate how simple it is to create a window using MFC classes without using the wizard
  • Demonstrate that compiling and linking an MFC program is as simple as a regular console program
  • Outline basic MFC concepts
#include <afxwin.h>
// declare a simple window
class MyWnd : public CWnd {
public:
    MyWnd() : CWnd()
    {}
    DECLARE_MESSAGE_MAP()
};
BEGIN_MESSAGE_MAP(MyWnd, CWnd)
END_MESSAGE_MAP()

//
// There must be one instance of a CWinApp derived class per program
class MyApp : public CWinApp {
    CWnd* m_pWnd;
public:
    MyApp()
        : CWinApp()
        , m_pWnd(0)
    {}
    BOOL InitInstance()
    {
        MyWnd* pWnd = new MyWnd;
        if (!pWnd->CreateEx(WS_EX_CLIENTEDGE,
            ::AfxRegisterWndClass(0, 0,
                 ::GetSysColorBrush(COLOR_WINDOW), 0),
            _T("My Window"),
            WS_OVERLAPPEDWINDOW,
            0,
            0,
            CW_USEDEFAULT,
            CW_USEDEFAULT,
            NULL,
            0) )
            return FALSE;
        m_pWnd = m_pMainWnd = pWnd;

        pWnd->ShowWindow(SW_SHOW);
        pWnd->UpdateWindow();

        return TRUE;
    }
    int ExitInstance()
    {
        delete m_pWnd; m_pWnd = 0;
        return 0;
    }
};
MyApp theApp;

Compile this using the following command line in Visual Studio 2005 Command Prompt:

cl /MT /EHsc /D "_UNICODE" /D "UNICODE" tut1.cpp /link /subsystem:windows 
   /ENTRY:"wWinMainCRTStartup"

MyWnd derives from CWnd which is the base MFC class that represents a window. All windowing classes are derived from CWnd including the standard Win32 controls such as Button, Listbox, etc. Right after its constructor, MyWnd declares a message map using the DECLARE_MESSAGE_MAP() macro. This macro expands to insert the necessary data members in the class declaration that would allow us to specify the class methods to be invoked for handling the various messages that are sent to the window. Specifying the individual method to be invoked in response to a windows message will be covered in the next tutorial.

Every MFC program requires a single instance of a CWinApp derived class. This forms the entry point into the program upon its startup. InitInstance() is this entry point and ExitInstance() being the exit point that is invoked when the user quits the program. This would be a good place to program specific initialization/de-initialization code.

Inside InitInstance(), we create the window by instantiating MyWnd object and calling the CWnd::CreateEx() method. The second parameter to CreateEx() is the return value from the call

::AfxRegisterWndClass(0, 0, ::GetSysColorBrush(COLOR_WINDOW), 0);

which essentialy returns an ATOM (a Win32 type) which specifies the default properties for a window (such as default style, cursor, background color, etc). Refer to the documentation of Win32 API RegisterClass() for more on window classes.