Ver Fonte

开始进行摄像头画区域

张洋 há 4 semanas atrás
pai
commit
03f35f4e43

+ 6 - 1
bin/Win32/Debug/zhipuzi_pos_windows/skin/setting_ai_shibie.xml

@@ -7,5 +7,10 @@
 	
 	<Control height="1" bkcolor="#FFEAEAEA" padding="0,20,0,0"></Control>
 	
-	
+	<HorizontalLayout height="500" padding="0,20,0,0">
+		<Label text="摄像头识别区域设置" width="260" height="30"/>
+		<VerticalLayout width="600" height="450" padding="10,0,10,0">
+			<Control name="image_video" float="true" pos="0,0,600,450"></Control>
+		</VerticalLayout>		
+	</HorizontalLayout>
 </Window>

+ 13 - 13
bin/Win32/Debug/zhipuzi_pos_windows/skin/setting_page.xml

@@ -4,31 +4,31 @@
 		<HorizontalLayout>
 			<HorizontalLayout width="350" bkcolor="#FFF6F6F6">
 				<VerticalLayout inset="0,0,0,0" vscrollbar="true">
-					<Option name="setting_waimai_order_switch" selected="true" text="外卖订单设置" group="setting_switch" align="center" height="69"  padding="0,0,0,0" selectedimage="file='white_bkg.png'" textcolor="#FF666666" selectedtextcolor="#FF000000" />
+					<Option name="setting_switch_page_waimai_order" selected="true" text="外卖订单设置" group="setting_switch" align="center" height="69"  padding="0,0,0,0" selectedimage="file='white_bkg.png'" textcolor="#FF666666" selectedtextcolor="#FF000000" />
 					<Control height="1" bkcolor="#FFD1D1D1"></Control>
-					<Option name="setting_diannei_order_switch" text="店内订单设置" group="setting_switch" align="center" height="69" padding="0,0,0,0" selectedimage="file='white_bkg.png'" textcolor="#FF666666" selectedtextcolor="#FF000000" />
+					<Option name="setting_switch_page_diannei_order" text="店内订单设置" group="setting_switch" align="center" height="69" padding="0,0,0,0" selectedimage="file='white_bkg.png'" textcolor="#FF666666" selectedtextcolor="#FF000000" />
 					<Control height="1" bkcolor="#FFD1D1D1"></Control>
-					<Option name="setting_printer_switch" text="小票打印设置" group="setting_switch" align="center" height="69" padding="0,0,0,0" selectedimage="file='white_bkg.png'" textcolor="#FF666666" selectedtextcolor="#FF000000" />
+					<Option name="setting_switch_page_printer" text="小票打印设置" group="setting_switch" align="center" height="69" padding="0,0,0,0" selectedimage="file='white_bkg.png'" textcolor="#FF666666" selectedtextcolor="#FF000000" />
 					<Control height="1" bkcolor="#FFD1D1D1"></Control>
-					<Option name="setting_biaoqian_printer_switch" text="标签打印设置" group="setting_switch" align="center" height="69" padding="0,0,0,0" selectedimage="file='white_bkg.png'" textcolor="#FF666666" selectedtextcolor="#FF000000" />
+					<Option name="setting_switch_page_biaoqian_printer" text="标签打印设置" group="setting_switch" align="center" height="69" padding="0,0,0,0" selectedimage="file='white_bkg.png'" textcolor="#FF666666" selectedtextcolor="#FF000000" />
 					<Control height="1" bkcolor="#FFD1D1D1"></Control>
-					<Option name="setting_chufang_printer_switch" text="厨房打印设置" group="setting_switch" align="center" height="69" padding="0,0,0,0" selectedimage="file='white_bkg.png'" textcolor="#FF666666" selectedtextcolor="#FF000000" />
+					<Option name="setting_switch_page_chufang_printer" text="厨房打印设置" group="setting_switch" align="center" height="69" padding="0,0,0,0" selectedimage="file='white_bkg.png'" textcolor="#FF666666" selectedtextcolor="#FF000000" />
 					<Control height="1" bkcolor="#FFD1D1D1"></Control>
-					<Option name="setting_dianzicheng_switch" text="称重电子秤设置" group="setting_switch" align="center" height="69" padding="0,0,0,0" selectedimage="file='white_bkg.png'" textcolor="#FF666666" selectedtextcolor="#FF000000" />
+					<Option name="setting_switch_page_dianzicheng" text="称重电子秤设置" group="setting_switch" align="center" height="69" padding="0,0,0,0" selectedimage="file='white_bkg.png'" textcolor="#FF666666" selectedtextcolor="#FF000000" />
 					<Control height="1" bkcolor="#FFD1D1D1"></Control>
-					<Option name="setting_tiaomacheng_switch" text="条码秤设置" group="setting_switch" align="center" height="69" padding="0,0,0,0" selectedimage="file='white_bkg.png'" textcolor="#FF666666" selectedtextcolor="#FF000000" />
+					<Option name="setting_switch_page_tiaomacheng" text="条码秤设置" group="setting_switch" align="center" height="69" padding="0,0,0,0" selectedimage="file='white_bkg.png'" textcolor="#FF666666" selectedtextcolor="#FF000000" />
 					<Control height="1" bkcolor="#FFD1D1D1"></Control>
-					<Option name="setting_ai_recognition_switch" text="AI识别设置" group="setting_switch" align="center" height="69" padding="0,0,0,0" selectedimage="file='white_bkg.png'" textcolor="#FF666666" selectedtextcolor="#FF000000" />
+					<Option name="setting_switch_page_ai_recognition" text="AI识别设置" group="setting_switch" align="center" height="69" padding="0,0,0,0" selectedimage="file='white_bkg.png'" textcolor="#FF666666" selectedtextcolor="#FF000000" />
 					<Control height="1" bkcolor="#FFD1D1D1"></Control>
-					<Option name="setting_xianshi_switch" text="显示设置" group="setting_switch" align="center" height="69" padding="0,0,0,0" selectedimage="file='white_bkg.png'" textcolor="#FF666666" selectedtextcolor="#FF000000" />
+					<Option name="setting_switch_page_xianshi" text="显示设置" group="setting_switch" align="center" height="69" padding="0,0,0,0" selectedimage="file='white_bkg.png'" textcolor="#FF666666" selectedtextcolor="#FF000000" />
 					<Control height="1" bkcolor="#FFD1D1D1"></Control>
-					<Option name="setting_tongbu_switch" text="数据同步" group="setting_switch" align="center" height="69" padding="0,0,0,0" selectedimage="file='white_bkg.png'" textcolor="#FF666666" selectedtextcolor="#FF000000" />
+					<Option name="setting_switch_page_tongbu" text="数据同步" group="setting_switch" align="center" height="69" padding="0,0,0,0" selectedimage="file='white_bkg.png'" textcolor="#FF666666" selectedtextcolor="#FF000000" />
 					<Control height="1" bkcolor="#FFD1D1D1"></Control>
-					<Option name="setting_kuaijiejian_switch" text="快捷键设置" group="setting_switch" align="center" height="69" padding="0,0,0,0" selectedimage="file='white_bkg.png'" textcolor="#FF666666" selectedtextcolor="#FF000000" />
+					<Option name="setting_switch_page_kuaijiejian" text="快捷键设置" group="setting_switch" align="center" height="69" padding="0,0,0,0" selectedimage="file='white_bkg.png'" textcolor="#FF666666" selectedtextcolor="#FF000000" />
 					<Control height="1" bkcolor="#FFD1D1D1"></Control>
-					<Option name="setting_system_switch" text="系统设置" group="setting_switch" align="center" height="69" padding="0,0,0,0" selectedimage="file='white_bkg.png'" textcolor="#FF666666" selectedtextcolor="#FF000000" />
+					<Option name="setting_switch_page_system" text="系统设置" group="setting_switch" align="center" height="69" padding="0,0,0,0" selectedimage="file='white_bkg.png'" textcolor="#FF666666" selectedtextcolor="#FF000000" />
 					<Control height="1" bkcolor="#FFD1D1D1"></Control>
-					<Option name="setting_system_about" text="关于" group="setting_switch" align="center" height="69" padding="0,0,0,0" selectedimage="file='white_bkg.png'" textcolor="#FF666666" selectedtextcolor="#FF000000" />
+					<Option name="setting_switch_page_system_about" text="关于" group="setting_switch" align="center" height="69" padding="0,0,0,0" selectedimage="file='white_bkg.png'" textcolor="#FF666666" selectedtextcolor="#FF000000" />
 					<Control height="1" bkcolor="#FFD1D1D1"></Control>
 
 					<Control height="30"></Control>

+ 568 - 70
zhipuzi_pos_windows/page/CSettingPageUI.cpp

@@ -18,6 +18,17 @@
 using boost::asio::ip::tcp;
 
 #include "../tool/CFoodHandle.h"
+#include "../tool/CAppEnv.h"
+#include "../tool/CVideoCapture.h"
+
+namespace
+{
+	const int kVideoPreviewWidth = 600;
+	const int kVideoPreviewHeight = 450;
+	const int kVideoMinWidth = 400;
+	const int kVideoMinHeight = 300;
+	const int kVideoGripSize = 8;
+}
 
 CSettingPageUI::CSettingPageUI()
 {
@@ -27,6 +38,19 @@ CSettingPageUI::CSettingPageUI()
 
 CSettingPageUI::~CSettingPageUI()
 {
+	m_is_show_video_capture = false;
+
+	if (m_hBitmap)
+	{
+		DeleteObject(m_hBitmap);
+		m_hBitmap = NULL;
+	}
+
+	if (m_hMemDC)
+	{
+		DeleteDC(m_hMemDC);
+		m_hMemDC = NULL;
+	}
 }
 
 //初始化当前页面的展示,处理默认展示效果,在页面被加载的时候调用
@@ -1698,80 +1722,18 @@ void CSettingPageUI::HandleSelectChangeMsg(TNotifyUI& msg)
 {
 	CDuiString name = msg.pSender->GetName();
 
-	//判断设置中心的tab
-	if (name == _T("setting_waimai_order_switch"))
-	{
-		CTabLayoutUI* pControl = static_cast<CTabLayoutUI*>(this->FindSubControl(_T("setting_switch")));
-		pControl->SelectItem(0);
-	}
-	if (name == _T("setting_diannei_order_switch"))
-	{
-		CTabLayoutUI* pControl = static_cast<CTabLayoutUI*>(this->FindSubControl(_T("setting_switch")));
-		pControl->SelectItem(1);
-	}
-	else if (name == _T("setting_printer_switch"))
-	{
-		CTabLayoutUI* pControl = static_cast<CTabLayoutUI*>(this->FindSubControl(_T("setting_switch")));
-		pControl->SelectItem(2);
-	}
-	else if (name == _T("setting_biaoqian_printer_switch"))
-	{
-		CTabLayoutUI* pControl = static_cast<CTabLayoutUI*>(this->FindSubControl(_T("setting_switch")));
-		pControl->SelectItem(3);
-	}
-	else if (name == _T("setting_chufang_printer_switch"))
-	{
-		CTabLayoutUI* pControl = static_cast<CTabLayoutUI*>(this->FindSubControl(_T("setting_switch")));
-		pControl->SelectItem(4);
-	}
-	else if (name == _T("setting_dianzicheng_switch"))
-	{
-		CTabLayoutUI* pControl = static_cast<CTabLayoutUI*>(this->FindSubControl(_T("setting_switch")));
-		pControl->SelectItem(5);
-	}
-	else if (name == _T("setting_tiaomacheng_switch"))
-	{
-		CTabLayoutUI* pControl = static_cast<CTabLayoutUI*>(this->FindSubControl(_T("setting_switch")));
-		pControl->SelectItem(6);
-	}
-	else if (name == _T("setting_ai_recognition_switch"))
-	{
-		CTabLayoutUI * pControl = static_cast<CTabLayoutUI *>(this->FindSubControl(_T("setting_switch")));
-		pControl->SelectItem(7);
-	}
-	else if (name == _T("setting_xianshi_switch"))
-	{
-		CTabLayoutUI* pControl = static_cast<CTabLayoutUI*>(this->FindSubControl(_T("setting_switch")));
-		pControl->SelectItem(8);
-	}
-	else if (name == _T("setting_tongbu_switch"))
-	{
-		CTabLayoutUI* pControl = static_cast<CTabLayoutUI*>(this->FindSubControl(_T("setting_switch")));
-		pControl->SelectItem(9);
+	std::wstring ws_name = name.GetData();
 
-		CLabelUI* label = static_cast<CLabelUI*>(this->FindSubControl(L"setting_tongbu_result"));
-		label->SetVisible(false);
-	}
-	else if (name == _T("setting_kuaijiejian_switch"))
+	if (ws_name.find(L"setting_switch_page_") == 0)
 	{
-		CTabLayoutUI* pControl = static_cast<CTabLayoutUI*>(this->FindSubControl(_T("setting_switch")));
-		pControl->SelectItem(10);
+		//说明是点击了切换设置页面
+		this->SwitchSettingPage(ws_name);
 
-		CButtonUI* focusEdit = static_cast<CButtonUI*>(this->FindSubControl(_T("setting_kuaijiejian_page_btn_weixinshoukuan")));
-		
-		m_pManager->SetFocus(NULL);
-	}
-	else if (name == _T("setting_system_switch"))
-	{
-		CTabLayoutUI* pControl = static_cast<CTabLayoutUI*>(this->FindSubControl(_T("setting_switch")));
-		pControl->SelectItem(11);
-	}
-	else if (name == _T("setting_system_about"))
-	{
-		CTabLayoutUI* pControl = static_cast<CTabLayoutUI*>(this->FindSubControl(_T("setting_switch")));
-		pControl->SelectItem(12);
+		return;
 	}
-	else if (name == _T("xianshi_setting_youtu"))
+
+	//判断设置中心非页面切换的tab
+	if (name == _T("xianshi_setting_youtu"))
 	{
 		CSetting::GetInstance()->SetParam("setting_xianshi_is_youtu", "1");
 	}
@@ -2113,6 +2075,8 @@ bool CSettingPageUI::HandleCustomMessage(UINT uMsg, WPARAM wParam, LPARAM lParam
 
 		label->SetText(L"同步成功!");
 		label->SetVisible(true);
+
+		return true;
 	}
 	else if (uMsg == WM_SETTING_TONGBU_FAIL)
 	{
@@ -2120,6 +2084,8 @@ bool CSettingPageUI::HandleCustomMessage(UINT uMsg, WPARAM wParam, LPARAM lParam
 
 		label->SetText(m_tongbu_error.c_str());
 		label->SetVisible(true);
+
+		return true;
 	}
 	else if (uMsg == WM_KEYDOWN || uMsg == WM_SYSKEYDOWN || uMsg == WM_KEYUP || uMsg == WM_SYSKEYUP)
 	{
@@ -2130,6 +2096,18 @@ bool CSettingPageUI::HandleCustomMessage(UINT uMsg, WPARAM wParam, LPARAM lParam
 			return OnHotKeyDown(uMsg, wParam, lParam);
 		}
 	}
+	else if (uMsg == WM_SETCURSOR || uMsg == WM_MOUSEMOVE || uMsg == WM_LBUTTONDOWN || uMsg == WM_LBUTTONUP)
+	{
+		if (HandleVideoBorderMouseMessage(uMsg, wParam, lParam))
+		{
+			return true;
+		}
+	}
+	else if (uMsg == WM_AI_XUEXI_UPDATE)
+	{
+		UpdateVideoShow();
+		return true;
+	}
 
 	return false;
 }
@@ -3380,4 +3358,524 @@ void CSettingPageUI::InitKuaijiejianShow()
 
 	CButtonUI* shoukuan_edit = static_cast<CButtonUI*>(this->FindSubControl(_T("setting_kuaijiejian_page_btn_tiaozhuan_shoukuan")));
 	shoukuan_edit->SetText(shoukuan_info);
+}
+
+void CSettingPageUI::SwitchSettingPage(std::wstring page_name)
+{
+	if (page_name == _T("setting_switch_page_waimai_order"))
+	{
+		CTabLayoutUI* pControl = static_cast<CTabLayoutUI*>(this->FindSubControl(_T("setting_switch")));
+		pControl->SelectItem(0);
+	}
+	if (page_name == _T("setting_switch_page_diannei_order"))
+	{
+		CTabLayoutUI* pControl = static_cast<CTabLayoutUI*>(this->FindSubControl(_T("setting_switch")));
+		pControl->SelectItem(1);
+	}
+	else if (page_name == _T("setting_switch_page_printer"))
+	{
+		CTabLayoutUI* pControl = static_cast<CTabLayoutUI*>(this->FindSubControl(_T("setting_switch")));
+		pControl->SelectItem(2);
+	}
+	else if (page_name == _T("setting_switch_page_biaoqian_printer"))
+	{
+		CTabLayoutUI* pControl = static_cast<CTabLayoutUI*>(this->FindSubControl(_T("setting_switch")));
+		pControl->SelectItem(3);
+	}
+	else if (page_name == _T("setting_switch_page_chufang_printer"))
+	{
+		CTabLayoutUI* pControl = static_cast<CTabLayoutUI*>(this->FindSubControl(_T("setting_switch")));
+		pControl->SelectItem(4);
+	}
+	else if (page_name == _T("setting_switch_page_dianzicheng"))
+	{
+		CTabLayoutUI* pControl = static_cast<CTabLayoutUI*>(this->FindSubControl(_T("setting_switch")));
+		pControl->SelectItem(5);
+	}
+	else if (page_name == _T("setting_switch_page_tiaomacheng"))
+	{
+		CTabLayoutUI* pControl = static_cast<CTabLayoutUI*>(this->FindSubControl(_T("setting_switch")));
+		pControl->SelectItem(6);
+	}
+	else if (page_name == _T("setting_switch_page_ai_recognition"))
+	{
+		CTabLayoutUI* pControl = static_cast<CTabLayoutUI*>(this->FindSubControl(_T("setting_switch")));
+		pControl->SelectItem(7);
+	}
+	else if (page_name == _T("setting_switch_page_xianshi"))
+	{
+		CTabLayoutUI* pControl = static_cast<CTabLayoutUI*>(this->FindSubControl(_T("setting_switch")));
+		pControl->SelectItem(8);
+	}
+	else if (page_name == _T("setting_switch_page_tongbu"))
+	{
+		CTabLayoutUI* pControl = static_cast<CTabLayoutUI*>(this->FindSubControl(_T("setting_switch")));
+		pControl->SelectItem(9);
+
+		CLabelUI* label = static_cast<CLabelUI*>(this->FindSubControl(L"setting_tongbu_result"));
+		label->SetVisible(false);
+	}
+	else if (page_name == _T("setting_switch_page_kuaijiejian"))
+	{
+		CTabLayoutUI* pControl = static_cast<CTabLayoutUI*>(this->FindSubControl(_T("setting_switch")));
+		pControl->SelectItem(10);
+
+		CButtonUI* focusEdit = static_cast<CButtonUI*>(this->FindSubControl(_T("setting_kuaijiejian_page_btn_weixinshoukuan")));
+
+		m_pManager->SetFocus(NULL);
+	}
+	else if (page_name == _T("setting_switch_page_system"))
+	{
+		CTabLayoutUI* pControl = static_cast<CTabLayoutUI*>(this->FindSubControl(_T("setting_switch")));
+		pControl->SelectItem(11);
+	}
+	else if (page_name == _T("setting_switch_page_system_about"))
+	{
+		CTabLayoutUI* pControl = static_cast<CTabLayoutUI*>(this->FindSubControl(_T("setting_switch")));
+		pControl->SelectItem(12);
+	}
+
+	if (page_name == _T("setting_switch_page_ai_recognition"))
+	{
+		if (m_is_show_video_capture == false)
+		{
+			//需要启动摄像头进程
+			std::thread(&CSettingPageUI::HandleUpdateVideo, this).detach();
+			CAppEnv::GetInstance()->AddWorkerNum();
+		}
+
+		m_is_show_video_capture = true;
+	}
+	else
+	{
+		m_is_show_video_capture = false;
+	}
+}
+
+void CSettingPageUI::HandleUpdateVideo()
+{
+	while (m_is_show_video_capture == true)
+	{
+		if (m_pMainWnd->GetCurPageName() != CMainWnd::SETTING)
+		{
+			//如果不是在设置页面,这个线程直接结束
+			break;
+		}
+
+		cv::Mat img;
+		CVideoCapture::GetInstance()->GetFrame(img);
+		if (img.empty())
+{
+			//摄像头有问题,等待一会再重新读取
+			Sleep(1000);
+			continue;
+		}
+
+		if (img.type() != CV_8UC3)
+		{
+			// 仅支持 3 通道彩色图像
+			cvtColor(img, img, cv::COLOR_GRAY2BGR);
+		}
+
+		//缩放尺寸,适合在界面上展示,过大过小都不好看,这个尺寸是根据界面上image控件的大小来定的,可以根据实际情况调整
+		cv::resize(img, img, cv::Size(kVideoPreviewWidth, kVideoPreviewHeight), 0, 0, cv::INTER_LINEAR);
+
+		// 创建或更新位图
+		HDC hDC = GetDC(NULL);
+		if (m_hBitmap)
+		{
+			DeleteObject(m_hBitmap);
+		}
+
+		BITMAPINFO bmi = { 0 };
+		bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
+		bmi.bmiHeader.biWidth = img.cols;
+		bmi.bmiHeader.biHeight = -img.rows; // 翻转 Y 轴(OpenCV 与 GDI 坐标方向相反)
+		bmi.bmiHeader.biPlanes = 1;
+		bmi.bmiHeader.biBitCount = 24;
+		bmi.bmiHeader.biCompression = BI_RGB;
+
+		m_hBitmap = CreateDIBitmap(hDC, &bmi.bmiHeader, CBM_INIT, img.data, &bmi, DIB_RGB_COLORS);
+		ReleaseDC(NULL, hDC);
+
+		//位图已经生成好,发送消息进行刷新显示
+		::PostMessage(m_pMainWnd->GetHWND(), WM_AI_XUEXI_UPDATE, 0, 0);
+
+		// 控制帧率,避免过度占用 CPU,30 FPS 大约是每 33 毫秒更新一次
+		Sleep(1000 / 30);
+	}
+
+	CAppEnv::GetInstance()->DelWorkerNum();
+}
+
+void CSettingPageUI::UpdateVideoShow()
+{
+	LoadVideoBorderRect();
+
+	HDC hDC = m_pManager->GetPaintDC();
+	if (!hDC || !m_hBitmap)
+	{
+		return;
+	}
+
+	if (!m_hMemDC)
+	{
+		m_hMemDC = CreateCompatibleDC(hDC);
+	}
+
+	CControlUI* pImage = static_cast<CControlUI*>(this->FindSubControl(_T("image_video")));
+	if (!pImage)
+	{
+		return;
+	}
+
+	BITMAP bm = { 0 };
+	GetObject(m_hBitmap, sizeof(BITMAP), &bm);
+	if (bm.bmWidth <= 0 || bm.bmHeight <= 0)
+	{
+		return;
+	}
+
+	HBITMAP hOldBitmap = (HBITMAP)SelectObject(m_hMemDC, m_hBitmap);
+
+	RECT imageRect = pImage->GetPos();
+	int imageWidth = imageRect.right - imageRect.left;
+	int imageHeight = imageRect.bottom - imageRect.top;
+	if (imageWidth <= 0 || imageHeight <= 0)
+	{
+		SelectObject(m_hMemDC, hOldBitmap);
+		return;
+	}
+
+	StretchBlt(hDC, imageRect.left, imageRect.top, imageRect.right - imageRect.left,
+		imageRect.bottom - imageRect.top, m_hMemDC, 0, 0, bm.bmWidth, bm.bmHeight, SRCCOPY);
+
+	RECT borderRect = GetVideoBorderDisplayRect(imageRect);
+
+	int savedDc = SaveDC(hDC);
+	IntersectClipRect(hDC, imageRect.left, imageRect.top, imageRect.right, imageRect.bottom);
+
+	HPEN hPen = CreatePen(PS_SOLID, 2, RGB(255, 0, 0));
+	HPEN hOldPen = (HPEN)SelectObject(hDC, hPen);
+	HBRUSH hOldBrush = (HBRUSH)SelectObject(hDC, GetStockObject(NULL_BRUSH));
+
+	Rectangle(hDC, borderRect.left, borderRect.top, borderRect.right, borderRect.bottom);
+	
+	HBRUSH hHandleBrush = CreateSolidBrush(RGB(255, 0, 0));
+	auto drawHandle = [&](int x, int y)
+		{
+			const int half = 4;
+			RECT rc = { x - half, y - half, x + half, y + half };
+			FillRect(hDC, &rc, hHandleBrush);
+		};
+
+	int cx = (borderRect.left + borderRect.right) / 2;
+	int cy = (borderRect.top + borderRect.bottom) / 2;
+
+	/*
+	drawHandle(borderRect.left, borderRect.top);
+	drawHandle(borderRect.right, borderRect.top);
+	drawHandle(borderRect.left, borderRect.bottom);
+	drawHandle(borderRect.right, borderRect.bottom);
+
+	drawHandle(cx, borderRect.top);
+	drawHandle(cx, borderRect.bottom);
+	drawHandle(borderRect.left, cy);
+	drawHandle(borderRect.right, cy);
+	*/
+
+	drawHandle(borderRect.left + 4, borderRect.top + 4);
+	drawHandle(borderRect.right - 4, borderRect.top + 4);
+	drawHandle(borderRect.left + 4, borderRect.bottom - 4);
+	drawHandle(borderRect.right - 4, borderRect.bottom - 4);
+
+	drawHandle(cx, borderRect.top + 4);
+	drawHandle(cx, borderRect.bottom - 4);
+	drawHandle(borderRect.left + 4, cy);
+	drawHandle(borderRect.right - 4, cy);
+
+	DeleteObject(hHandleBrush);
+	SelectObject(hDC, hOldBrush);
+	SelectObject(hDC, hOldPen);
+	DeleteObject(hPen);
+
+	RestoreDC(hDC, savedDc);
+
+	SelectObject(m_hMemDC, hOldBitmap);
+}
+
+void CSettingPageUI::StopVideoThread()
+{
+	m_is_show_video_capture = false;
+}
+
+void CSettingPageUI::LoadVideoBorderRect()
+{
+	if (m_videoBorderLoaded)
+	{
+		return;
+	}
+
+	auto readInt = [](const std::string& value, int defaultValue) -> int
+		{
+			if (value.empty())
+			{
+				return defaultValue;
+			}
+
+			return atoi(value.c_str());
+		};
+
+	int x = readInt(CSetting::GetInstance()->GetParam("setting_ai_video_rect_x"), 0);
+	int y = readInt(CSetting::GetInstance()->GetParam("setting_ai_video_rect_y"), 0);
+	int w = readInt(CSetting::GetInstance()->GetParam("setting_ai_video_rect_w"), kVideoPreviewWidth);
+	int h = readInt(CSetting::GetInstance()->GetParam("setting_ai_video_rect_h"), kVideoPreviewHeight);
+
+	if (w < kVideoMinWidth) w = kVideoMinWidth;
+	if (h < kVideoMinHeight) h = kVideoMinHeight;
+	if (w > kVideoPreviewWidth) w = kVideoPreviewWidth;
+	if (h > kVideoPreviewHeight) h = kVideoPreviewHeight;
+
+	if (x < 0) x = 0;
+	if (y < 0) y = 0;
+	if (x + w > kVideoPreviewWidth) x = kVideoPreviewWidth - w;
+	if (y + h > kVideoPreviewHeight) y = kVideoPreviewHeight - h;
+
+	m_videoBorderRect.left = x;
+	m_videoBorderRect.top = y;
+	m_videoBorderRect.right = x + w;
+	m_videoBorderRect.bottom = y + h;
+
+	m_videoBorderLoaded = true;
+}
+
+void CSettingPageUI::SaveVideoBorderRect()
+{
+	int x = m_videoBorderRect.left;
+	int y = m_videoBorderRect.top;
+	int w = m_videoBorderRect.right - m_videoBorderRect.left;
+	int h = m_videoBorderRect.bottom - m_videoBorderRect.top;
+
+	CSetting::GetInstance()->SetParam("setting_ai_video_rect_x", std::to_string(x));
+	CSetting::GetInstance()->SetParam("setting_ai_video_rect_y", std::to_string(y));
+	CSetting::GetInstance()->SetParam("setting_ai_video_rect_w", std::to_string(w));
+	CSetting::GetInstance()->SetParam("setting_ai_video_rect_h", std::to_string(h));
+}
+
+RECT CSettingPageUI::GetVideoImageRect()
+{
+	RECT rc = { 0, 0, 0, 0 };
+
+	CControlUI* pImage = static_cast<CControlUI*>(this->FindSubControl(_T("image_video")));
+	if (!pImage)
+	{
+		return rc;
+	}
+
+	return pImage->GetPos();
+}
+
+RECT CSettingPageUI::GetVideoBorderDisplayRect(const RECT& imageRect)
+{
+	RECT rc = { 0, 0, 0, 0 };
+
+	int imageWidth = imageRect.right - imageRect.left;
+	int imageHeight = imageRect.bottom - imageRect.top;
+	if (imageWidth <= 0 || imageHeight <= 0)
+	{
+		return rc;
+	}
+
+	rc.left = imageRect.left + MulDiv(m_videoBorderRect.left, imageWidth, kVideoPreviewWidth);
+	rc.top = imageRect.top + MulDiv(m_videoBorderRect.top, imageHeight, kVideoPreviewHeight);
+	rc.right = imageRect.left + MulDiv(m_videoBorderRect.right, imageWidth, kVideoPreviewWidth);
+	rc.bottom = imageRect.top + MulDiv(m_videoBorderRect.bottom, imageHeight, kVideoPreviewHeight);
+
+	return rc;
+}
+
+int CSettingPageUI::HitTestVideoBorder(POINT pt, const RECT& borderRect)
+{
+	bool nearLeft = (std::abs(pt.x - borderRect.left) <= kVideoGripSize) &&
+		(pt.y >= borderRect.top - kVideoGripSize) &&
+		(pt.y <= borderRect.bottom + kVideoGripSize);
+
+	bool nearRight = (std::abs(pt.x - borderRect.right) <= kVideoGripSize) &&
+		(pt.y >= borderRect.top - kVideoGripSize) &&
+		(pt.y <= borderRect.bottom + kVideoGripSize);
+
+	bool nearTop = (std::abs(pt.y - borderRect.top) <= kVideoGripSize) &&
+		(pt.x >= borderRect.left - kVideoGripSize) &&
+		(pt.x <= borderRect.right + kVideoGripSize);
+
+	bool nearBottom = (std::abs(pt.y - borderRect.bottom) <= kVideoGripSize) &&
+		(pt.x >= borderRect.left - kVideoGripSize) &&
+		(pt.x <= borderRect.right + kVideoGripSize);
+
+	if (nearLeft && nearTop) return HTTOPLEFT;
+	if (nearRight && nearTop) return HTTOPRIGHT;
+	if (nearLeft && nearBottom) return HTBOTTOMLEFT;
+	if (nearRight && nearBottom) return HTBOTTOMRIGHT;
+
+	if (nearLeft && pt.y >= borderRect.top && pt.y <= borderRect.bottom) return HTLEFT;
+	if (nearRight && pt.y >= borderRect.top && pt.y <= borderRect.bottom) return HTRIGHT;
+	if (nearTop && pt.x >= borderRect.left && pt.x <= borderRect.right) return HTTOP;
+	if (nearBottom && pt.x >= borderRect.left && pt.x <= borderRect.right) return HTBOTTOM;
+
+	return HTNOWHERE;
+}
+
+bool CSettingPageUI::HandleVideoBorderMouseMessage(UINT uMsg, WPARAM wParam, LPARAM lParam)
+{
+	if (!m_is_show_video_capture)
+	{
+		return false;
+	}
+
+	LoadVideoBorderRect();
+
+	RECT imageRect = GetVideoImageRect();
+	int imageWidth = imageRect.right - imageRect.left;
+	int imageHeight = imageRect.bottom - imageRect.top;
+	if (imageWidth <= 0 || imageHeight <= 0)
+	{
+		return false;
+	}
+
+	RECT borderRect = GetVideoBorderDisplayRect(imageRect);
+
+	auto setCursorByHit = [](int hitType) -> bool
+		{
+			LPCTSTR cursorId = IDC_ARROW;
+
+			switch (hitType)
+			{
+				case HTLEFT:
+				case HTRIGHT:
+					cursorId = IDC_SIZEWE;
+					break;
+				case HTTOP:
+				case HTBOTTOM:
+					cursorId = IDC_SIZENS;
+					break;
+				case HTTOPLEFT:
+				case HTBOTTOMRIGHT:
+					cursorId = IDC_SIZENWSE;
+					break;
+				case HTTOPRIGHT:
+				case HTBOTTOMLEFT:
+					cursorId = IDC_SIZENESW;
+					break;
+				default:
+					return false;
+			}
+
+			::SetCursor(::LoadCursor(NULL, cursorId));
+			return true;
+		};
+
+	if (uMsg == WM_SETCURSOR)
+	{
+		if (LOWORD(lParam) != HTCLIENT)
+		{
+			return false;
+		}
+
+		POINT pt;
+		::GetCursorPos(&pt);
+		::ScreenToClient(m_pMainWnd->GetHWND(), &pt);
+
+		int hitType = m_isDraggingVideoBorder ? m_videoBorderHitType : HitTestVideoBorder(pt, borderRect);
+		return setCursorByHit(hitType);
+	}
+
+	POINT pt = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) };
+
+	if (uMsg == WM_LBUTTONDOWN)
+	{
+		int hitType = HitTestVideoBorder(pt, borderRect);
+		if (hitType == HTNOWHERE)
+		{
+			return false;
+		}
+
+		m_isDraggingVideoBorder = true;
+		m_videoBorderHitType = hitType;
+		m_videoBorderDragStartPt = pt;
+		m_videoBorderDragStartRect = m_videoBorderRect;
+
+		::SetCapture(m_pMainWnd->GetHWND());
+		setCursorByHit(hitType);
+		return true;
+	}
+
+	if (uMsg == WM_MOUSEMOVE)
+	{
+		if (!m_isDraggingVideoBorder)
+		{
+			int hitType = HitTestVideoBorder(pt, borderRect);
+			if (hitType == HTNOWHERE)
+			{
+				return false;
+			}
+
+			setCursorByHit(hitType);
+			return true;
+		}
+
+		int dx = MulDiv(pt.x - m_videoBorderDragStartPt.x, kVideoPreviewWidth, imageWidth);
+		int dy = MulDiv(pt.y - m_videoBorderDragStartPt.y, kVideoPreviewHeight, imageHeight);
+
+		RECT newRect = m_videoBorderDragStartRect;
+
+		if (m_videoBorderHitType == HTLEFT || m_videoBorderHitType == HTTOPLEFT || m_videoBorderHitType == HTBOTTOMLEFT)
+		{
+			int maxLeft = static_cast<int>(m_videoBorderDragStartRect.right) - kVideoMinWidth;
+			int left = std::max(0, std::min(static_cast<int>(m_videoBorderDragStartRect.left) + dx, maxLeft));
+			newRect.left = static_cast<LONG>(left);
+		}
+
+		if (m_videoBorderHitType == HTRIGHT || m_videoBorderHitType == HTTOPRIGHT || m_videoBorderHitType == HTBOTTOMRIGHT)
+		{
+			int minRight = static_cast<int>(m_videoBorderDragStartRect.left) + kVideoMinWidth;
+			int right = std::max(minRight, std::min(static_cast<int>(m_videoBorderDragStartRect.right) + dx, kVideoPreviewWidth));
+			newRect.right = static_cast<LONG>(right);
+		}
+
+		if (m_videoBorderHitType == HTTOP || m_videoBorderHitType == HTTOPLEFT || m_videoBorderHitType == HTTOPRIGHT)
+		{
+			int maxTop = static_cast<int>(m_videoBorderDragStartRect.bottom) - kVideoMinHeight;
+			int top = std::max(0, std::min(static_cast<int>(m_videoBorderDragStartRect.top) + dy, maxTop));
+			newRect.top = static_cast<LONG>(top);
+		}
+
+		if (m_videoBorderHitType == HTBOTTOM || m_videoBorderHitType == HTBOTTOMLEFT || m_videoBorderHitType == HTBOTTOMRIGHT)
+		{
+			int minBottom = static_cast<int>(m_videoBorderDragStartRect.top) + kVideoMinHeight;
+			int bottom = std::max(minBottom, std::min(static_cast<int>(m_videoBorderDragStartRect.bottom) + dy, kVideoPreviewHeight));
+			newRect.bottom = static_cast<LONG>(bottom);
+		}
+
+		m_videoBorderRect = newRect;
+
+		::PostMessage(m_pMainWnd->GetHWND(), WM_AI_XUEXI_UPDATE, 0, 0);
+		setCursorByHit(m_videoBorderHitType);
+		return true;
+	}
+
+	if (uMsg == WM_LBUTTONUP)
+	{
+		if (!m_isDraggingVideoBorder)
+		{
+			return false;
+		}
+
+		m_isDraggingVideoBorder = false;
+		::ReleaseCapture();
+		SaveVideoBorderRect();
+		::PostMessage(m_pMainWnd->GetHWND(), WM_AI_XUEXI_UPDATE, 0, 0);
+		return true;
+	}
+
+	return false;
 }

+ 25 - 32
zhipuzi_pos_windows/page/CSettingPageUI.h

@@ -8,66 +8,59 @@ public:
 	CSettingPageUI();
 	~CSettingPageUI();
 
-	//初始化页面控件,页面创建构造的时候调用一次,注意和InitShow的区别,这个不触发控件的任何事件
 	void InitControl();
-
-	//初始化当前页面的展示,处理默认展示效果,在页面每次被选中加载(注意不是页面创建构造)的时候调用,如果多次选中会多次调用,这里要避免数据重复处理
 	void InitShow();
-
-	//处理按钮点击类事件
 	void HandleClickMsg(TNotifyUI& msg);
-
-	//处理option切换事件
 	void HandleSelectChangeMsg(TNotifyUI& msg);
-
-	//处理下拉框、radio的切换事件
 	void HandleItemSelectMsg(TNotifyUI& msg);
-
-	//处理编辑框输入内容改变事件
 	void HandleTextChangedMsg(TNotifyUI& msg);
-
-	//处理扫码枪捕捉到的扫码信息
 	void HandleTextCapture(std::string content);
-
-	//处理自定义消息,各个页面处理各个页面自己的(是自己处理的消息返回true,不是自己处理的消息返回false)
 	bool HandleCustomMessage(UINT uMsg, WPARAM wParam, LPARAM lParam);
 
-	//开始同步商品到标签秤
 	void StartTongbuBiaoqiancheng();
-
 	void HandleTongbuBiaoqiancheng();
-
-	//开始同步热键到标签秤
 	void StartTongbuRejian();
-
 	void HandleTongbuRejian();
-
-	//开始清空标签秤已有PLU
 	void StartQiongkongPLU();
-
 	void HandleQiongkongPLU();
-
 	void StartTongbu();
-
 	void HandleTongbu();
 
 	bool OnHotKeyDown(UINT uMsg, WPARAM wParam, LPARAM lParam);
 
 	void InitKuaijiejianShow();
 
+	void HandleUpdateVideo();
+	void UpdateVideoShow();
+	void StopVideoThread();
+
 private:
-	std::wstring m_errinfo;
+	void SwitchSettingPage(std::wstring page_name);
+
+	void LoadVideoBorderRect();
+	void SaveVideoBorderRect();
+	RECT GetVideoImageRect();
+	RECT GetVideoBorderDisplayRect(const RECT& imageRect);
+	int HitTestVideoBorder(POINT pt, const RECT& borderRect);
+	bool HandleVideoBorderMouseMessage(UINT uMsg, WPARAM wParam, LPARAM lParam);
 
+private:
+	std::wstring m_errinfo;
 	std::wstring m_tongbu_error;
 
-	//设置快捷键相关
 	UINT m_mod = 0;
 	UINT m_vk = 0;
 	bool m_isSet = false;
-
-	/*
-	 *当前设置快捷键的状态 1:微信收款 2会员登录 3会员收款 4现金收款 5钱箱 6退出登录 7点单页面 8订单页面 9会员页面 10报表页面 11交班页面
-	 *12设置页面 13商品页面 14库存页面 15采购页面 16桌台页面 17收款页面
-	 **/
 	int m_kuaijiejian_type;
+
+	bool m_is_show_video_capture = false;
+	HBITMAP m_hBitmap = NULL;
+	HDC m_hMemDC = NULL;
+
+	RECT m_videoBorderRect = { 0, 0, 400, 300 };
+	RECT m_videoBorderDragStartRect = { 0, 0, 400, 300 };
+	POINT m_videoBorderDragStartPt = { 0, 0 };
+	bool m_videoBorderLoaded = false;
+	bool m_isDraggingVideoBorder = false;
+	int m_videoBorderHitType = HTNOWHERE;
 };

BIN
zhipuzi_pos_windows/resource/skin.zip


+ 1 - 1
zhipuzi_pos_windows/tool/CVideoCapture.cpp

@@ -85,7 +85,7 @@ void CVideoCapture::InitVideoCapture()
 
 	// ========== 关键硬件参数:解决反光过曝发白 ==========
 	// 1. 关闭自动曝光(必须,否则强光自动提亮加重发白)
-	m_cap.set(cv::CAP_PROP_AUTO_EXPOSURE, 1);
+	//m_cap.set(cv::CAP_PROP_AUTO_EXPOSURE, 1);
 	// 2. 手动降低曝光值,数值越小进光越少,根据相机微调
 	//m_cap.set(cv::CAP_PROP_EXPOSURE, -6);
 	// 3. 降低增益ISO,减少高光噪点饱和

+ 4 - 0
zhipuzi_pos_windows/wnd/CMainWnd.cpp

@@ -77,6 +77,10 @@ void CMainWnd::End()
 	//停止AI学习页面的摄像头线程
 	CAIxuexiPageUI* page = static_cast<CAIxuexiPageUI*>(this->GetPage(CMainWnd::AIXUEXI));
 	page->StopVideoThread();
+
+	//停止设置页面的摄像头线程
+	CSettingPageUI* settingPage = static_cast<CSettingPageUI*>(this->GetPage(CMainWnd::SETTING));
+	settingPage->StopVideoThread();
 }
 
 void CMainWnd::UpdateFoodImage()

+ 0 - 2
zhipuzi_pos_windows/worker/CMqttClientWorker.cpp

@@ -80,7 +80,6 @@ void CMqttClientWorker::Run()
 
 	//代码走到这里,说明退出登录了,要暂停推送了
 
-	/*
 	try
 	{
 		LOG_INFO("Disconnecting from the MQTT server...");
@@ -92,7 +91,6 @@ void CMqttClientWorker::Run()
 		LOG_INFO(("disconnect error, exc:" + exc.get_message()).c_str());
 		CLewaimaiLog::OutputDebugMessage(("disconnect error, exc:" + exc.get_message()).c_str());
 	}
-	*/
 
 	//销毁客户端
 	delete m_async_client;