一起来搭建像Qt一样的win32图形框架

10 篇文章 0 订阅
2 篇文章 0 订阅

哎,就文章标题就纠结的很。其实就是觉得Qt的图形框架很爽,但有时候想写一个工具,用Qt带的dll就一堆,有点不爽。又不想用MFC,所以就有了这篇文章。

大家都知道在Qt中的main函数超级简单

int main()
{
  QApplication app;
  MainWindow w;
  w.show();
  return app.exec();
}

大家是不是觉得Qt做得很棒呢!如果我们的win32可以这样,那不是也好爽。下面我们就一步一步来搭建这个框架。

首先用VS建立一个win32工程。为了说明,我调整了代码并删除了一些不必要的代码。

LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	switch (message)
	{
	case WM_DESTROY:
		PostQuitMessage(0);
		break;
	default:
		return DefWindowProc(hWnd, message, wParam, lParam);
	}
	return 0;
}

int APIENTRY _tWinMain(_In_ HINSTANCE hInstance,
                     _In_opt_ HINSTANCE hPrevInstance,
                     _In_ LPTSTR    lpCmdLine,
                     _In_ int       nCmdShow)
{
	WNDCLASSEX wcex;
	wcex.cbSize = sizeof(WNDCLASSEX);
	wcex.style = CS_HREDRAW | CS_VREDRAW;
	wcex.lpfnWndProc = WndProc;
	wcex.cbClsExtra = 0;
	wcex.cbWndExtra = 0;
	wcex.hInstance = hInstance;
	wcex.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_WIN32PROJECT7));
	wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
	wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
	wcex.lpszMenuName = MAKEINTRESOURCE(IDC_WIN32PROJECT7);
	wcex.lpszClassName = L"123";
	wcex.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));
	RegisterClassEx(&wcex);

	HWND hWnd = CreateWindow(L"123", L"title", WS_OVERLAPPEDWINDOW,
		CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, hInstance, NULL);

	ShowWindow(hWnd, nCmdShow);
	UpdateWindow(hWnd);

	HACCEL hAccelTable = 0;
	MSG		msg;
	// 主消息循环: 
	while (GetMessage(&msg, NULL, 0, 0))
	{
		if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
		{
			TranslateMessage(&msg);
			DispatchMessage(&msg);
		}
	}

	return (int) msg.wParam;
}

从上面的代码需要关注几个关键点:

1 调用RegisterClassEx注册窗口类。只需要调用一次。

2 调用CreateWindow创建窗口。

3 使用GetMessage进行消息循环。

4 实现窗口的回调函数WndProc。

如果要实现像Qt那样的架构。RegisterClassEx当然要放到Application中,CreateWindow当然要放到MainWindow中,因为可以创建多个窗口,GetMessage也只能放到Application中。准确地说是放到Application的exec中。至于WndProc放到哪,现在先不管。

千言万语,不如代码来得实在。下面我们来时Application类类似Qt的QApplication类。修改后的代码如下:

LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	switch (message)
	{
	case WM_DESTROY:
		PostQuitMessage(0);
		break;
	default:
		return DefWindowProc(hWnd, message, wParam, lParam);
	}
	return 0;
}

class Application{
public:
	Application(){
		WNDCLASSEX wcex;
		wcex.cbSize = sizeof(WNDCLASSEX);
		wcex.style = CS_HREDRAW | CS_VREDRAW;
		wcex.lpfnWndProc = WndProc;
		wcex.cbClsExtra = 0;
		wcex.cbWndExtra = 0;
		wcex.hInstance = NULL;
		wcex.hIcon = NULL;
		wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
		wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
		wcex.lpszMenuName = MAKEINTRESOURCE(IDC_WIN32PROJECT7);
		wcex.lpszClassName = L"123";
		wcex.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));
		RegisterClassEx(&wcex);
	}

	int exec(){
		HACCEL hAccelTable = 0;
		MSG		msg;
		while (GetMessage(&msg, NULL, 0, 0))
		{
			if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
			{
				TranslateMessage(&msg);
				DispatchMessage(&msg);
			}
		}
		return (int)msg.wParam;
	}

};



int APIENTRY _tWinMain(_In_ HINSTANCE hInstance,
                     _In_opt_ HINSTANCE hPrevInstance,
                     _In_ LPTSTR    lpCmdLine,
                     _In_ int       nCmdShow)
{
	Application app;

	HWND hWnd = CreateWindow(L"123", L"title", WS_OVERLAPPEDWINDOW,
		CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, hInstance, NULL);

	ShowWindow(hWnd, nCmdShow);
	UpdateWindow(hWnd);

	return app.exec();
}

哈哈,是不是发现main函数一下子简单了好多,并且有点像Qt的框架了!别着急,我们继续。

CreateWindow放到一个窗口类中这可以搞定,但如何把回调函数也放到里面呢?怎么办?怎么办?嘿,我想到一个好办法,在Application实现一个static函数作为全局的回调函数,然后CreateWindow时记录所有窗口的句柄,然后Application的Proc不是就可以分发消息到不同的窗口类了嘛!呵呵,说干就干。


#include <cassert>
#include <unordered_map>

class Wnd;

class Application{
public:
 Application(){
  WNDCLASSEX wcex;
  wcex.cbSize = sizeof(WNDCLASSEX);
  wcex.style = CS_HREDRAW | CS_VREDRAW;
  wcex.lpfnWndProc = Application::proc;
  wcex.cbClsExtra = 0;
  wcex.cbWndExtra = 0;
  wcex.hInstance = NULL;
  wcex.hIcon = NULL;
  wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
  wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
  wcex.lpszMenuName = MAKEINTRESOURCE(IDC_WIN32PROJECT7);
  wcex.lpszClassName = L"123";
  wcex.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));
  RegisterClassEx(&wcex);
 }

 int exec(){
  HACCEL hAccelTable = 0;
  MSG  msg;
  while (GetMessage(&msg, NULL, 0, 0))
  {
   if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
   {
    TranslateMessage(&msg);
    DispatchMessage(&msg);
   }
  }
  return (int)msg.wParam;
 }

 static void registerWnd(Wnd* wnd);
 static void unregisterWnd(Wnd* wnd);
 static Wnd* wnd(HWND hWnd);
 static HRESULT CALLBACK proc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);

private:
 static std::unordered_map<HWND, Wnd*> s_wnds;
};
std::unordered_map<HWND, Wnd*> Application::s_wnds;

class Wnd{
public:
 Wnd() :_hWnd(NULL){}
 virtual ~Wnd(){
  Application::unregisterWnd(this);
  DestroyWindow(_hWnd);
 }
 void createWindow(LPCWSTR className, LPCWSTR windowName, DWORD style, int x, int y, int w, int h, HWND hParent, HMENU hMenu){
  _hWnd = CreateWindow(className, windowName, style, x, y, w, h, hParent, hMenu,
   GetModuleHandle(NULL), 0);
  Application::registerWnd(this);
 }
 HWND hWnd()const{ return _hWnd; }

 void show(){ ShowWindow(_hWnd, SW_SHOW); }

 virtual HRESULT proc(UINT message, WPARAM wParam, LPARAM lParam){ return 0; }

protected:
 HWND _hWnd;
};

void Application::registerWnd(Wnd* wnd){
 assert(wnd);
 s_wnds[wnd->hWnd()] = wnd;
}
void Application::unregisterWnd(Wnd* wnd){
 auto itr = s_wnds.find(wnd->hWnd());
 if (itr != s_wnds.end()){
  s_wnds.erase(itr);
 }
}
Wnd* Application::wnd(HWND hWnd){
 auto itr = s_wnds.find(hWnd);
 if (itr != s_wnds.end())
  return itr->second;
 return nullptr;
}
HRESULT CALLBACK Application::proc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam){
 auto wnd = Application::wnd(hWnd);
 if (wnd){
  return wnd->proc(message, wParam, lParam);
 }
 return DefWindowProc(hWnd, message, wParam, lParam);
}

class MainWindow :public Wnd{
public:
 MainWindow(){
  createWindow(L"123", L"title", WS_OVERLAPPEDWINDOW,
   CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL);
 }

 HRESULT proc(UINT message, WPARAM wParam, LPARAM lParam){
  switch (message)
  {
  case WM_DESTROY:
   PostQuitMessage(0);
   break;
  default:
   return DefWindowProc(_hWnd, message, wParam, lParam);
  }
  return 0;
 }
};


int APIENTRY _tWinMain(_In_ HINSTANCE hInstance,
                     _In_opt_ HINSTANCE hPrevInstance,
                     _In_ LPTSTR    lpCmdLine,
                     _In_ int       nCmdShow)
{
 Application app;

 MainWindow w;
 w.show();

 return app.exec();
}

看,现在基本上像Qt了,注意注册类时的回调函数已经变了哈!现在集成MainWindow然后实现proc处理函数就好了,当然还有更好的方法!好了,其实接下来还有好多工作,比如按钮等控件的布局,类似QLayout,按钮等事件如mouseClickEvent等。我要回家了,改天在写吧!




  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: QT是一款跨平台的C++应用程序开发框架,在搭建一套库存管理系统方面具有很大的优势。下面是关于如何使用QT搭建库存管理系统的简要步骤: 1. 数据库设计:首先,需要设计一个数据库来存储库存管理系统所需的数据,包括产品信息、库存数量、进货与销售记录等。可以使用SQLite数据库作为本地数据库或者MySQL等作为远程数据库。 2. 界面设计:使用QT提供的界面开发工具,设计一个用户友好的界面。可以使用QT的UI设计师来快速创建和管理界面。 3. 功能模块划分:根据库存管理系统的需求,将其拆分为不同的功能模块,例如产品管理、进货管理、销售管理、库存查询等。每个功能模块对应一个或多个界面和相关的业务逻辑。 4. 业务逻辑开发:根据需求,使用C++语言编写相应的业务逻辑代码。例如,在产品管理模块中,需要实现产品的新增、编辑和删除功能;在进货管理模块中,需要实现进货记录的添加和更新功能等。 5. 数据库连接:使用QT提供的数据库模块,与所选用的数据库建立连接,并编写相关的数据库操作代码,实现数据的增删改查等功能。 6. 用户交互:在界面上添加必要的用户交互功能,例如按钮、菜单、表格等,使用户可以方便地进行数据录入、查询和操作。 7. 数据校验与处理:在数据录入的过程中,进行数据校验和处理,确保数据的有效性和准确性。例如,对于产品的数量字段,需要进行数据范围的校验。 8. 报表生成:根据需求,编写报表生成的代码,将库存、进货、销售等数据按照需求生成相应的报表,方便管理者进行数据分析和决策。 以上是使用QT搭建一套库存管理系统的大致步骤,具体的实现过程需要根据实际需求进行设计和开发。QT提供了丰富的开发工具和库函数,使得开发库存管理系统变得更加简单和高效。 ### 回答2: 首先,要搭建一套基于Qt的库存管理系统,我们需要考虑以下几个方面: 1. 界面设计:使用Qt的界面设计功能来创建一个用户友好的界面,可以包括菜单、工具栏、表格、图表等元素,以方便用户进行库存管理操作。 2. 数据库设计:使用Qt的数据库模块连接和操作数据库,设计库存管理系统所需的数据库表结构,其中包括商品信息、库存数量、进货信息、出货信息等。 3. 功能实现:根据实际需求,使用Qt的编程能力实现库存管理系统的各项功能,如添加新商品、编辑商品信息、查询库存数量、记录进货和出货等操作。 4. 数据统计与分析:利用Qt的图表和数据处理功能,对库存管理系统中的数据进行统计和分析,生成销售报表、库存报表等,以帮助用户更好地了解库存情况和经营状况。 5. 部署与交付:将搭建好的库存管理系统打包成可执行文件或生成安装程序,以便用户可以方便地部署和使用。 总结起来搭建一套基于Qt的库存管理系统需要涉及界面设计、数据库设计、功能实现、数据统计与分析等方面,通过充分利用Qt的强大功能和丰富的工具,可以快速高效地构建一个功能完备、界面友好、易于使用的库存管理系统。 ### 回答3: Qt是一款跨平台的C++开发框架,具有图形界面设计能力和丰富的功能库,可以用于搭建库存管理系统。以下是关于如何使用Qt搭建一套库存管理系统的简要介绍。 首先,我们需要定义库存管理系统的功能和需求。比如,我们需要记录和管理产品的信息,包括产品的名称、编号、价格、库存数量等。同时,我们还需要能够进行产品的出库和入库操作,计算库存余量,并生成相应的报表。 使用Qt框架,我们可以利用Qt图形界面设计功能来创建使用者友好的界面。我们可以使用Qt的窗口类(如QMainWindow)来创建主界面,并在界面上添加所需的各种控件(如文本框、按钮、表格等)来显示和输入产品信息。我们还可以使用Qt的信号和槽机制来实现用户与界面之间的交互,比如点击按钮触发相应的操作。 在储存产品信息方面,我们可以使用Qt的数据库模块(如QSqlDatabase)来连接数据库,并使用SQL语句来创建和管理产品信息表。我们可以利用Qt的表格控件(如QTableView)来显示产品信息,并提供相应的操作(比如新增、编辑和删除)来对产品信息进行管理。 对于出库和入库操作,我们可以在界面上添加相应的按钮,并在点击按钮时触发相应的操作。比如,点击“出库”按钮时,系统可以弹出一个对话框供用户输入出库商品的数量,然后更新库存数量并生成相应的出库记录。同样地,对于入库操作,系统也可以通过类似的方式实现。 最后,我们可以使用Qt的绘图功能来生成报表。根据需要,我们可以使用Qt提供的绘图类(如QChart)来创建柱状图或折线图,并根据产品的库存数量或其他指标来可视化数据。 总之,通过使用Qt框架,我们可以在图形界面操作的基础上,利用丰富的功能库和数据库模块来搭建一套库存管理系统,满足产品信息的记录、出库入库操作及报表生成等功能需求。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值