MFC模型树控件TreeCtrl实现按下Ctrl键多选,按下Shift键连选

2023-05-16

MFC模型树控件TreeCtrl实现按下Ctrl键多选,按下Shift键连选


  MFC的List Box只需要将控件属性中的Selection项设置为Extended,即可实现Ctrl键多选和Shift键连选;然而MFC的Tree Contrl(TreeCtrl)没有像List Box那样设置某个属性就实现Ctrl键多选和Shift键连选的功能,网上大多数MFC的TreeCtrl多选功能是用复选框实现的,也有前辈想通过继承类CTreeCtrl来重写多选功能,其中实现该功能较好的示例为以下链接:http://www.pudn.com/Download/item/id/640887.html,经过试验,发现该示例按住Ctrl只能选中两个项目。于是在此示例的基础上,我重新写了按下Ctrl键的代码,实现了真正的多选。
  上述链接中包含一个基于对话框的MFC应用程序示例,该示例演示了CTreeCtrEx的用法,要将CTreeCtrlEx合并到自己的项目中,请按照以下步骤操作:
  (1)将TreeCtrlEx.cpp和TreeCtrlEx.h两个文件复制到项目目录;
  (2)将它们添加到项目中;
  (3)在需要的地方包含TreeCtrlEx.h(包含在StdAfx.h中非常方便);
  (4)在需要多选的地方,将对象的类型由CTreeCtrl更改为CTreeCtrlEx。
  TreeCtrlEx.cpp和TreeCtrlEx.h中只有以下6个函数是多选所需要的,其他的可根据自己的需要保留:

afx_msg void OnLButtonDown(UINT nFlags, CPoint point);
afx_msg void OnLButtonUp(UINT nFlags, CPoint point);
afx_msg void OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags);
BOOL SelectItems(HTREEITEM hFromItem, HTREEITEM hToItem);
void ClearSelection(BOOL bMultiOnly = FALSE);
void SelectMultiple(HTREEITEM hClickedItem, UINT nFlags, CPoint point);

  下面直接上我修改后TreeCtrlEx.cpp和TreeCtrlEx.h中的代码,有什么不对的地方,还请读者留言,大家一起学习。代码仅供参考,转载请标明出处。
  
TreeCtrlEx.h

#pragma once
#include "afxcmn.h"
#include "vector"
using namespace std;
class CTreeCtrlEx :
	public CTreeCtrl
{
public:
	CTreeCtrlEx();
	~CTreeCtrlEx();
	DECLARE_MESSAGE_MAP()
	afx_msg void OnLButtonDown(UINT nFlags, CPoint point);
	afx_msg void OnLButtonUp(UINT nFlags, CPoint point);
	afx_msg void OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags);
	BOOL SelectItems(HTREEITEM hFromItem, HTREEITEM hToItem);
	void ClearSelection(BOOL bMultiOnly = FALSE);
protected:
	void SelectMultiple(HTREEITEM hClickedItem, UINT nFlags, CPoint point);
public:
	BOOL		m_bEditLabelPending;
	UINT		m_idTimer;
	HTREEITEM	m_hClickedItem;
	BOOL		m_bSelectPending;
	CPoint		m_ptClick;
	HTREEITEM	m_hFirstSelectedItem;
	BOOL		m_bSelectionComplete;
	bool		bIsSelectedItem[100];//存放每一个选项的状态
};

TreeCtrlEx.cpp

#include "stdafx.h"
#include "TreeCtrlEx.h"

#define TCEX_EDITLABEL 1		// Edit label timer event

CTreeCtrlEx::CTreeCtrlEx()
{
	m_bSelectPending = FALSE;
	m_hClickedItem = NULL;
	m_hFirstSelectedItem = NULL;
	m_bSelectionComplete = TRUE;
	m_bEditLabelPending = FALSE;
	for (int i = 0; i < 100; i++)
	{
		bIsSelectedItem[i] = false;//初始化
	}
}


CTreeCtrlEx::~CTreeCtrlEx()
{
}
BEGIN_MESSAGE_MAP(CTreeCtrlEx, CTreeCtrl)
	ON_WM_LBUTTONDOWN()
	ON_WM_LBUTTONUP()
	ON_WM_KEYDOWN()
END_MESSAGE_MAP()


void CTreeCtrlEx::OnLButtonDown(UINT nFlags, CPoint point)
{
	// TODO:  在此添加消息处理程序代码和/或调用默认值
	UINT nHitFlags = 0;
	HTREEITEM hClickedItem = HitTest(point, &nHitFlags);

	//必须显式调用标签编辑,OnLButtonDown通常会这样做,但我们不能在这里调用它,因为有多个选择...
	if (!(nFlags&(MK_CONTROL | MK_SHIFT)) && (GetStyle() & TVS_EDITLABELS) && (nHitFlags & TVHT_ONITEMLABEL))
	if (hClickedItem == GetSelectedItem())
	{
		//在编辑标签之前清除多重选择
		ClearSelection();
		SelectItem(hClickedItem);
		//调用标签编辑
		m_bEditLabelPending = TRUE;
		m_idTimer = SetTimer(TCEX_EDITLABEL, GetDoubleClickTime(), NULL);
		return;
	}

	m_bEditLabelPending = FALSE;

	if (nHitFlags & TVHT_ONITEM)
	{
		SetFocus();
		m_hClickedItem = hClickedItem;
		//点击的项目是否已被选中
		BOOL bIsClickedItemSelected = GetItemState(hClickedItem, TVIS_SELECTED) & TVIS_SELECTED;
		if (bIsClickedItemSelected)
		{
			//可能用户想拖放多个项目!所以,等到OnLButtonUp()才进行选择。
			m_bSelectPending = TRUE;
		}
		else
		{
			SelectMultiple(hClickedItem, nFlags, point);
			m_bSelectPending = FALSE;
		}
		m_ptClick = point;
	}
	else
		CTreeCtrl::OnLButtonDown(nFlags, point);
}


void CTreeCtrlEx::OnLButtonUp(UINT nFlags, CPoint point)
{
	// TODO:  在此添加消息处理程序代码和/或调用默认值
	if (m_bSelectPending)
	{
		//一个项目一直在等待执行
		SelectMultiple(m_hClickedItem, nFlags, point);
		m_bSelectPending = FALSE;
	}

	m_hClickedItem = NULL;

	CTreeCtrl::OnLButtonUp(nFlags, point);
}


void CTreeCtrlEx::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags)
{
	// TODO:  在此添加消息处理程序代码和/或调用默认值
	CWnd* pWnd = GetParent();

	if (nChar == VK_NEXT || nChar == VK_PRIOR)
	{
		if (!(GetKeyState(VK_SHIFT) & 0x8000))
		{
			//用户在没有按住Shift键的情况下按PG键:清除多选(如果有多选),让基类做正常的选择工作!
			if (GetSelectedCount()>1)
				ClearSelection(TRUE);

			CTreeCtrl::OnKeyDown(nChar, nRepCnt, nFlags);
			m_hFirstSelectedItem = GetSelectedItem();
			return;
		}

		//表示选择过程未完成的标志(将禁止TVN_SELCHANGED发送给父类)
		m_bSelectionComplete = FALSE;

		//让基类选择项
		CTreeCtrl::OnKeyDown(nChar, nRepCnt, nFlags);
		HTREEITEM hSelectedItem = GetSelectedItem();

		//然后选择介于两者之间的项目
		SelectItems(m_hFirstSelectedItem, hSelectedItem);

		//选择过程现已完成。由于我们已经销毁了Windows的treectrl提供的TVN_SELCHANGED通知,
		//现在我们必须自己生成一个通知,这样我们的父类才能知道选择的更改。
		m_bSelectionComplete = TRUE;

		if (pWnd)
		{
			NM_TREEVIEW tv;
			memset(&tv.itemOld, 0, sizeof(tv.itemOld));

			tv.hdr.hwndFrom = GetSafeHwnd();
			tv.hdr.idFrom = GetWindowLong(GetSafeHwnd(), GWL_ID);
			tv.hdr.code = TVN_SELCHANGED;

			tv.itemNew.hItem = hSelectedItem;
			tv.itemNew.state = GetItemState(hSelectedItem, 0xffffffff);
			tv.itemNew.lParam = GetItemData(hSelectedItem);
			tv.itemNew.mask = TVIF_HANDLE | TVIF_STATE | TVIF_PARAM;

			tv.action = TVC_UNKNOWN;

			pWnd->SendMessage(WM_NOTIFY, tv.hdr.idFrom, (LPARAM)&tv);
		}
	}
	else if (nChar == VK_UP || nChar == VK_DOWN)
	{
		//获取当前选择的项目
		HTREEITEM hSelectedItem = GetSelectedItem();

		HTREEITEM hNextItem;
		if (nChar == VK_UP)
			hNextItem = GetPrevVisibleItem(hSelectedItem);
		else
			hNextItem = GetNextVisibleItem(hSelectedItem);

		if (!(GetKeyState(VK_SHIFT) & 0x8000))
		{
			//用户在没有按住Shift键的情况下按下箭头键:清除多项选择(如果多项选择),
			//并让基类执行正常的选择工作!
			if (GetSelectedCount()>1)
				ClearSelection(TRUE);

			if (hNextItem)
				CTreeCtrl::OnKeyDown(nChar, nRepCnt, nFlags);
			m_hFirstSelectedItem = GetSelectedItem();
			return;
		}

		if (hNextItem)
		{
			//表示选择过程未完成的标志。
			//(将禁止TVN_SELCHANGED发送给父类)
			m_bSelectionComplete = FALSE;

			//如果已经选择了下一项,我们假设用户在选择中“向后移动”,因此我们应该清除对上一项的选择
			BOOL bSelect = !(GetItemState(hNextItem, TVIS_SELECTED) & TVIS_SELECTED);

			//选择下一项(这也会取消选择前一项!)
			SelectItem(hNextItem);

			//现在,重新选择之前选择的项目
			if (bSelect || (!(GetItemState(hSelectedItem, TVIS_SELECTED) & TVIS_SELECTED)))
				SelectItems(m_hFirstSelectedItem, hNextItem);

			//选择过程现已完成。由于我们已经销毁了Windows的treectrl提供的TVN_SELCHANGED通知,
			//现在我们必须自己生成一个通知,这样我们的父类才能知道选择的更改。
			m_bSelectionComplete = TRUE;

			if (pWnd)
			{
				NM_TREEVIEW tv;
				memset(&tv.itemOld, 0, sizeof(tv.itemOld));

				tv.hdr.hwndFrom = GetSafeHwnd();
				tv.hdr.idFrom = GetWindowLong(GetSafeHwnd(), GWL_ID);
				tv.hdr.code = TVN_SELCHANGED;

				tv.itemNew.hItem = hNextItem;
				tv.itemNew.state = GetItemState(hNextItem, 0xffffffff);
				tv.itemNew.lParam = GetItemData(hNextItem);
				tv.itemNew.mask = TVIF_HANDLE | TVIF_STATE | TVIF_PARAM;

				tv.action = TVC_UNKNOWN;

				pWnd->SendMessage(WM_NOTIFY, tv.hdr.idFrom, (LPARAM)&tv);
			}
		}

		//由于本例中未调用基类的OnKeyDown(),
		//我们必须向父类提供我们自己的TVN_KEYDOWN通知
		CWnd* pWnd = GetParent();
		if (pWnd)
		{
			NMTVKEYDOWN tvk;

			tvk.hdr.hwndFrom = GetSafeHwnd();
			tvk.hdr.idFrom = GetWindowLong(GetSafeHwnd(), GWL_ID);
			tvk.hdr.code = TVN_KEYDOWN;

			tvk.wVKey = nChar;
			tvk.flags = 0;

			pWnd->SendMessage(WM_NOTIFY, tvk.hdr.idFrom, (LPARAM)&tvk);
		}
	}
	else
		//行为正常
		CTreeCtrl::OnKeyDown(nChar, nRepCnt, nFlags);
}


void CTreeCtrlEx::SelectMultiple(HTREEITEM hClickedItem, UINT nFlags, CPoint point)
{
	//开始准备NM_TreeView结构,以便在选择完成后发送通知
	NM_TREEVIEW tv;
	memset(&tv.itemOld, 0, sizeof(tv.itemOld));

	CWnd* pWnd = GetParent();

	HTREEITEM hOldItem = GetSelectedItem();

	if (hOldItem)
	{
		tv.itemOld.hItem = hOldItem;
		tv.itemOld.state = GetItemState(hOldItem, 0xffffffff);
		tv.itemOld.lParam = GetItemData(hOldItem);
		tv.itemOld.mask = TVIF_HANDLE | TVIF_STATE | TVIF_PARAM;
	}

	//表示选择过程未完成的标志。
	//(将禁止TVN_SELCHANGED发送给父类)
	m_bSelectionComplete = FALSE;

	//操作取决于用户是按住Shift键还是Ctrl键
	if (nFlags & MK_SHIFT)
	{
		//从第一个选定项目选择到单击的项目
		if (!m_hFirstSelectedItem)
			m_hFirstSelectedItem = GetSelectedItem();

		SelectItems(m_hFirstSelectedItem, hClickedItem);
	}
	else if (nFlags & MK_CONTROL)
	{
		//只有一个根节点时,存储该根节点下的所有子节点;
		//当有多个根节点时,还需其他方法存储所有节点,
		//可以参考《MFC之TreeCtrl遍历所有节点》https://www.cnblogs.com/HelloQLQ/p/12678753.html
		vector<HTREEITEM>vecItem;
		HTREEITEM root = GetRootItem();//若存在多个根节点,获取的是第一个根节点
		HTREEITEM child = GetChildItem(root);
		while (child != NULL)
		{
			vecItem.push_back(child);
			child = GetNextItem(child, TVGN_NEXT);
		}

		for (int i = 0; i < vecItem.size(); i++)
		{
			bIsSelectedItem[i] = GetItemState(vecItem[i], TVIS_SELECTED) & TVIS_SELECTED;
		}
		//CString cStr;//查看根节点下的子节点个数
		//cStr.Format(_T("%d"), vecItem.size());
		//MessageBox(cStr);

		BOOL bIsClickedItemSelected = GetItemState(hClickedItem, TVIS_SELECTED) & TVIS_SELECTED;

		//必须合成TVN_SELCHANGING通知
		if (pWnd)
		{
			tv.hdr.hwndFrom = GetSafeHwnd();
			tv.hdr.idFrom = GetWindowLong(GetSafeHwnd(), GWL_ID);
			tv.hdr.code = TVN_SELCHANGING;

			tv.itemNew.hItem = hClickedItem;
			tv.itemNew.state = GetItemState(hClickedItem, 0xffffffff);
			tv.itemNew.lParam = GetItemData(hClickedItem);

			tv.itemOld.hItem = NULL;
			tv.itemOld.mask = 0;

			tv.action = TVC_BYMOUSE;

			tv.ptDrag.x = point.x;
			tv.ptDrag.y = point.y;

			pWnd->SendMessage(WM_NOTIFY, tv.hdr.idFrom, (LPARAM)&tv);
		}

		for (int i = 0; i < vecItem.size(); i++)
		{
			if (bIsSelectedItem[i] && vecItem[i] == hClickedItem)
				SetItemState(vecItem[i], 0, TVIS_SELECTED);
			else if (bIsSelectedItem[i] && vecItem[i] != hClickedItem)
				SetItemState(vecItem[i], TVIS_SELECTED, TVIS_SELECTED);
			else if (!bIsSelectedItem[i] && vecItem[i] == hClickedItem)
				SetItemState(vecItem[i], TVIS_SELECTED, TVIS_SELECTED);
			else if (!bIsSelectedItem[i] && vecItem[i] != hClickedItem)
				SetItemState(vecItem[i], 0, TVIS_SELECTED);
		}
	}
	else
	{
		//首先清除所有“多选”项目的选择
		ClearSelection();

		//然后选择单击的项目
		SelectItem(hClickedItem);
		SetItemState(hClickedItem, TVIS_SELECTED, TVIS_SELECTED);

		//存储为第一个选定项目
		m_hFirstSelectedItem = hClickedItem;
	}

	//选择流程现已完成。由于我们已经销毁了Windows的treectrl提供的TVN_SELCHANGED通知,
	//现在我们必须自己生成一个通知。
	//这样我们的父类就可以知道选择的更改。
	m_bSelectionComplete = TRUE;

	if (pWnd)
	{
		tv.hdr.hwndFrom = GetSafeHwnd();
		tv.hdr.idFrom = GetWindowLong(GetSafeHwnd(), GWL_ID);
		tv.hdr.code = TVN_SELCHANGED;

		tv.itemNew.hItem = m_hClickedItem;
		tv.itemNew.state = GetItemState(m_hClickedItem, 0xffffffff);
		tv.itemNew.lParam = GetItemData(m_hClickedItem);
		tv.itemNew.mask = TVIF_HANDLE | TVIF_STATE | TVIF_PARAM;

		tv.action = TVC_UNKNOWN;

		pWnd->SendMessage(WM_NOTIFY, tv.hdr.idFrom, (LPARAM)&tv);
	}
}


BOOL CTreeCtrlEx::SelectItems(HTREEITEM hFromItem, HTREEITEM hToItem)
{
	//确定选择方向
	//(查看树中排在第一位的项目)
	HTREEITEM hItem = GetRootItem();

	while (hItem && hItem != hFromItem && hItem != hToItem)
		hItem = GetNextVisibleItem(hItem);

	if (!hItem)
		return FALSE;	//树中不可见的项目

	BOOL bReverse = hItem == hToItem;

	// "Really" select the 'to' item 这将取消选择之前选择的项

	SelectItem(hToItem);

	//再次浏览所有可见项目,然后选择/取消选择

	hItem = GetRootItem();
	BOOL bSelect = FALSE;

	while (hItem)
	{
		if (hItem == (bReverse ? hToItem : hFromItem))
			bSelect = TRUE;

		if (bSelect)
		{
			if (!(GetItemState(hItem, TVIS_SELECTED) & TVIS_SELECTED))
				SetItemState(hItem, TVIS_SELECTED, TVIS_SELECTED);
		}
		else
		{
			if (GetItemState(hItem, TVIS_SELECTED) & TVIS_SELECTED)
				SetItemState(hItem, 0, TVIS_SELECTED);
		}

		if (hItem == (bReverse ? hFromItem : hToItem))
			bSelect = FALSE;

		hItem = GetNextVisibleItem(hItem);
	}
	return TRUE;
}


void CTreeCtrlEx::ClearSelection(BOOL bMultiOnly/*=FALSE*/)
{
	for (HTREEITEM hItem = GetRootItem(); hItem != NULL; hItem = GetNextVisibleItem(hItem))
	if (GetItemState(hItem, TVIS_SELECTED) & TVIS_SELECTED)
		SetItemState(hItem, 0, TVIS_SELECTED);
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

MFC模型树控件TreeCtrl实现按下Ctrl键多选,按下Shift键连选 的相关文章

  • Python的函数装饰器,@staticmethod、@classmethod 和 @property

    什么是Python 的 函数装饰器 xff1f Python 内置的 3 种函数装饰器 xff0c 分别是 xff20 staticmethod xff20 classmethod 和 64 property 那么 xff0c 函数装饰器的
  • C++11:原子交换函数compare_exchange_weak和compare_exchange_strong

    我们知道在C 43 43 11中引入了mutex和方便优雅的lock guard 但是有时候我们想要的是性能更高的无锁实现 xff0c 下面我们来讨论C 43 43 11中新增的原子操作类Atomic xff0c 我们可以利用它巧妙地实现无
  • C++11条件变量:notify_one()与notify_all()的区别

    notify one 与notify all 常用来唤醒阻塞的线程 notify one xff1a 因为只唤醒等待队列中的第一个线程 xff1b 不存在锁争用 xff0c 所以能够立即获得锁 其余的线程不会被唤醒 xff0c 需要等待再次
  • 数据库:group by 的使用

    一 概述 group by的意思是根据by对数据按照哪个字段进行分组 xff0c 或者是哪几个字段进行分组 二 语法 select 字段 from 表名 where 条件 group by 字段 或者 select 字段 from 表名 g
  • C++中 std::vector 的6种初始化方法

    1 vector lt int gt list1 默认初始化 xff0c 最常用 此时 xff0c vector为空 xff0c size为0 xff0c 表明容器中没有元素 xff0c 而且 capacity 也返回 0 xff0c 意味
  • MIMO雷达处理1

    参考文献 MIMO RADAR SIGNAL PROCESSING 以下为我自己的理解 xff0c 如有问题 xff0c 请指出 目录 初步分析虚拟阵列123 确认目标数 初步分析 MIMO radar与相控阵雷达区别在于MIMO中的各天线
  • AndroidStudio生成aar包和如何使用aar包

    我用的是android studio 2 0正式版 1 简介 aar包是Android studio下打包android工程中src res lib后生成的aar文件 xff0c aar包导入其他android studio 工程后 xff
  • C++智能指针详解:shared_ptr

    C 43 43 没有内存回收机制 xff0c 每次程序员new出来的对象需要手动delete xff0c 流程复杂时可能会漏掉delete xff0c 导致内存泄漏 于是C 43 43 引入智能指针 xff0c 可用于动态资源管理 xff0
  • C++算法题:关于树的算法

    问题1 xff1a 输入一棵二元查找树 xff0c 将该二元查找树转换成一个排序的双向链表 要求不能创建任何新的结点 xff0c 只调整指针的指向 什么是二元查找树 xff1f 比如 xff1a 转换成双向链表的顺序是 xff1a 1 3
  • C++算法题:递归和栈的算法

    问题1 xff1a 跳台阶问题 具体描述 xff0c 一个台阶总共有n级 xff0c 如果一次可以跳1级 xff0c 也可以跳2级 求总共有多少总跳法 xff0c 并分析算法的时间杂度 相当于从下往上跳 xff0c 最后剩一个 xff08
  • Linux的.service服务 实现程序开机自启

    一 service文件的位置 所有可用的单元文件存放在 lib systemd system 和 etc systemd system 目录 我们需要在 lib systemd system 下存放 service文件 xff0c 当sys
  • 计算机网络 复习提纲(完整版)

    第一章 概述 计算机网络 xff1a 利用通信线路和通信设备 xff0c 将地理位置和功能不同的多台计算机互联起来 xff0c 用完善的网络软件实现资源共享和信息传递的网络 组成 xff1a 计算机 xff0c 网络操作系统 xff0c 传
  • 无人机多任务寻径仿真软件与实验平台(一)

    项目背景 xff1a 近年来 xff0c 无人机的应用领域已经得到了极大的拓展 xff0c 旋翼无人机凭借较大的载荷 xff0c 稳定的飞行状态与对高空环境的高度适应 xff0c 成为了应用最为广泛的一类无人机 在欧洲 xff0c 北美洲等
  • 无人机实验平台(七) 实验平台的坐标转换(上)

    为什么要做坐标转换 xff1f 大疆提供的sdk中指出 xff0c 无人机通常情况下通过两个坐标系来控制方向 xff1a 自身坐标系 xff08 Body xff09 和地面坐标系 xff08 N E D xff09 这两个坐标系可以相互转
  • 无人机实验平台(十) 室内悬停问题

    VirtualStick Mode VirtualStick Mode是Sdk提供的一种底层控制模式 xff0c 通过模拟物理摇杆的运动向无人机按照一定频率发送摇杆差分信息 xff0c 使得无人机按照类似于被物理摇杆控制的方式运行 xff0
  • 第30章 ADC—电压采集—零死角玩转STM32-F429系列

    第30章 ADC 电压采集 全套 200 集视频教程和 1000 页 PDF 教程请到秉火论坛下载 xff1a www firebbs cn 野火视频教程优酷观看网址 xff1a http i youku com firege 本章参考资料
  • 大数据安全考试提纲

    范围划得虽大 xff0c 但是主题不多 xff0c 和往常一样记录一下重点 考试范围 1 大数据安全概念及目标 2 传统访问控制技术和基于密码的访问控制技术 3 角色挖掘的算法 4 对称密码 xff0c 非对称密码 xff0c hash算法
  • 算法设计与分析(小总结)

    原先一直没注意这一门 xff0c 大概是因为学这些算法的时候 xff0c 板子都不知道背多少遍了 可是考试毕竟不一样 xff0c 如果来个证明很容易抓瞎 看了往年的试题 xff0c 可能确实因为难度高 xff0c 范围一缩再缩 xff0c
  • 软件测试技术

    引论 为什么进行软件测试 xff1f 软件存在缺陷 xff0c 只有通过测试才可以发现缺陷 xff0c 只有发现缺陷才能把缺陷从产品中清除出去 软件中缺陷带来的损失是巨大的 测试是所有工程学科的基本组成单元 软件测试 xff1a 验证 43
  • c语言自定义tcp协议实现socket通信(windows版本)

    前面一篇博客介绍了mac linux下通过C语言自定义协议实现socket通信的示例 xff0c 因为大部分api与windows还有很多区别 xff0c 这里就特意把windows下的tcp通信实例给介绍一下 无论是linux xff0c

随机推荐

  • opencv for java给图片添加水印中文问题

    opencv提供的给图片添加文字的方法Imgproc putText 可以给图片添加文字 xff0c 最后类似一个添加水印的效果 xff0c 但是这个方法对中文支持不好 xff0c 在没有字体支持的情况下 xff0c 默认中文显示 如下图所
  • 社区版Intellij IDEA安装Spring Boot Assistant插件解决yml无提示问题

    如题所示 xff0c 我们如果个人使用免费社区版的IDEA xff0c 它缺失了很多功能 xff0c 使用起来没有专业版那么强大 xff0c 比如无法直接创建springbootinit项目 xff0c spring配置yml文件没有提示
  • IDEA安装spotbugs插件替代findbugs插件

    相信最近想在IDEA上安装findbugs插件的朋友 xff0c 遇到与我一样的问题 xff0c findbugs与IDEA不兼容 xff1a https plugins jetbrains com plugin 3847 findbugs
  • 第40章 CAN—通讯实验—零死角玩转STM32-F429系列

    第40章 CAN 通讯实验 全套 200 集视频教程和 1000 页 PDF 教程请到秉火论坛下载 xff1a www firebbs cn 野火视频教程优酷观看网址 xff1a http i youku com firege 本章参考资料
  • easyexcel读取excel合并单元格数据

    普通的excel列表 xff0c easyexcel读取是没有什么问题的 但是 xff0c 如果有合并单元格 xff0c 那么它读取的时候 xff0c 能获取数据 xff0c 但是数据是不完整的 如下所示的单元格数据 xff1a 我们通过简
  • clion + opencv环境搭建

    clion是一个jetbrains提供的c 43 43 开发环境 xff0c 和idea pycharm等开发工具类似 xff0c 界面有很多相似的地方 clion内置了一个mingw的编译环境 xff0c 自带了gcc g 43 43 等
  • ChatGLM-6B本地cpu部署

    ChatGLM 6B是清华团队研发的机器人对话系统 xff0c 类似ChatGPT xff0c 但是实际相差很多 xff0c 可以当作一个简单的ChatGPT ChatGLM部署默认是支持GPU加速 xff0c 内存需要32G以上 普通的机
  • java通过HttpServletRequest获取post请求中的body内容

    在java web应用中 xff0c 我们如何获取post请求body中的内容 xff1f 以及需要注意的问题 通常利用request获取参数可以直接通过req getParameter name 的方式获取url上面或者ajax data
  • Your password does not satisfy the current policy requirements解决办法

    mysql5 7 x安装以后 xff0c 想修改随机生成的密码为简单容易记忆的密码 xff0c 如root 123456等 xff0c 这时候通过修改密码的几种方式都不行 xff0c 出现密码不符合当前安全策略要求 为了解决这种问题 xff
  • 用一条SQL语句查询出每门课都大于80分的学生姓名

    两道sql题 xff1a 1 用一条SQL语句查询出每门课都大于80分的学生姓名 2 删除除了自动编号不同 xff0c 其他都相同的学生冗余信息 第一题数据如下 xff1a 分析 xff1a 每门课都大于80分 xff0c 就是说学生最低分
  • ajax通过post方法传数组

    ajax在web项目开发中经常会用到 xff0c 平时我们传递数据 xff0c 基本都是一个参数名对应一个参数值 xff0c 后端通过参数名就可以得到参数 xff0c 从而进行相关逻辑处理 xff0c 但是有时候我们会遇到批量操作 xff0
  • “操作无法完成因为其中的文件夹或文件已在另一个程序中打开”解决办法

    在windows系统中 xff0c 我们经常会遇到这样一个问题 xff1a 删除某一个文件或者文件夹 xff0c 被提醒 xff1a 操作无法完成 xff0c 因为其中的文件夹或文件已在另一个程序中打开 这个时候我们一般会先检查是否真的有程
  • 几种常见mybatis分页实现

    mybatis框架分页实现 xff0c 有几种方式 xff0c 最简单的就是利用原生的sql关键字limit来实现 xff0c 还有一种就是利用interceptor来拼接sql xff0c 实现和limit一样的功能 xff0c 再一个就
  • curl发送POST方法类型带body参数请求以及发送上传文件请求

    curl在实际中会被postman等替代 xff0c 但是他仍然是一个快速的模拟http请求的工具 xff0c 而且也有他不可替代的理由 xff0c 使用简单 xff0c 在命令行下就可以完成 今天不是介绍如何使用curl模拟各种GET P
  • 第45章 DCMI—OV2640摄像头—零死角玩转STM32-F429系列

    第45章 DCMI OV2640摄像头 全套 200 集视频教程和 1000 页 PDF 教程请到秉火论坛下载 xff1a www firebbs cn 野火视频教程优酷观看网址 xff1a http i youku com firege
  • 技术分享|探究群体智能—基于UWB定位技术的无人机集群协同

    随着无人机性能水平提高 xff0c 无人机在民用类和军事类活动中的使用越来越广泛 由于无人机飞行环境和任务难度不同 xff0c 单架无人机因为自身动力和负荷能力很难独自完成任务 xff0c 无人机集群协同作业则是当前科技现代化的任务要求 集
  • 技术分享 | 基于室外RTK/GPS定位系统下的无人机集群协同

    在自然界中 xff0c 为弥补个体能力的不足 xff0c 诸多生物种群能通过个体相互之间的交流与合作呈现出某种群体行为 xff0c 比如鱼群结群游弋 鸟群聚集迁徙以及蚂蚁协同搬运等 受此激励 xff0c 人们希望开发像鸟群 鱼群一样自由集结
  • STL学习系列之一——标准模板库STL介绍

    说明 xff1a 此文为连载版 xff0c 今天学习STL的介绍 1 STL介绍 标准模板库STL是当今每个从事C 43 43 编程的人需要掌握的技术 xff0c 所有很有必要总结下 本文将介绍STL并探讨它的三个主要概念 xff1a 容器
  • 如何在cmakelist中,find_package取指定路径中查找

    在开发中 xff0c 最常遇到环境问题 xff0c 这个opencv版本不对 xff0c 那个pcl版本不对 xff0c 如果将原系统目录下的库卸载后重装新的版本 xff0c 往往会出现一些意想不到的事 xff0c 费时费力 我常常这样做
  • MFC模型树控件TreeCtrl实现按下Ctrl键多选,按下Shift键连选

    MFC模型树控件TreeCtrl实现按下Ctrl键多选 xff0c 按下Shift键连选 MFC的List Box只需要将控件属性中的Selection项设置为Extended xff0c 即可实现Ctrl键多选和Shift键连选 xff1