基于C++(MFC)图形编辑界面工具
MFC 图形编辑界面工具
一、背景
喔,五天的实训终于结束了,学校安排的这次实训课名称叫高级程序设计实训,但在我看来,主要是学习了 Visual C++ .NET 所提供的 MFC(Microsoft Foundation Class)库所提供的类及其功能函数的使用。写这一篇博客的目的是针对实训中出现的问题做一些说明,方便以后查看,并且对这次实训做一些总结。这一次的实训对我来说其实挺难受的,真正用来学习使用 VS 和 MFC 的时间只有三天,加上下个周是考试周,还有几门课没有复习完,这几天基本上是连轴转,中午也泡在实验室里,唉啊还是自己太菜了。最后我们需要提交一个课程设计程序,因为时间的原因,我选择了最简单的图形界面编辑工具,这个程序其实在 C++ 的课程设计上就有这个,但当时我还不会 windows 图形界面的编程,现在想想这两个课程设计其实完全可以是一份(捂脸)。
在功能上:
- 能够在 windows 的界面下画图,能够画直线、空心矩形、、圆角矩形、空心圆形、填充矩形、填充圆形、填充圆角矩形和文字。
- 能够改变画图是画笔用的颜色、线宽、线型和填充用的颜色、字体。
- 能够保存、打开所做的图形文件
- 拥有菜单、工具栏、鼠标右键等编辑界面。
二、程序说明
1.工具栏说明
2.画图菜单
在画图菜单下,能够选择画直线、空心矩形、空心圆形、空心圆角矩形、填充矩形、填充圆形、填充圆角矩形。
3.文本菜单
文本输入菜单下有两个选项一个是文本输入、一个是字体设置,分别对应着两个对话框。
文本输入对话框,能够根据指定的 x、y 横纵坐标来定位输入位置,打印输入的相应信息。
而字体设置,调用系统自带的对话框,完成对字体类型、字形和字体大小的设置。
4.画笔设置菜单
画笔设置菜单下有画笔颜色、画笔类型、画笔宽度三个选项。其中画笔类型又包含实线、虚线、点线、点划线、双点划线五个选项。画笔类型根据查阅课本内容和上网搜索得知,只有在宽度为 1 的时候,才能显示除实线外的其他画笔类型,当宽度大于 1 时画出来的都是实线类型的线条。
颜色设置,调用系统自带的对话框,完成对画笔、画刷颜色的选择。同时选用该对话框能够实现自定义颜色。
画笔宽度设置对话框是自己设置的对话框,输入相应的画笔宽度,实现画笔宽度的改变。
5.界面下鼠标右键
右击鼠标会有鼠标右键菜单,其功能选项与功能栏所给的功能是一样的,选择画直线、空心矩形、空心圆形、空心圆角矩形、填充矩形、填充圆形、填充圆角矩形和文本。
三、鼠标拖动绘画
该程序的基础功能就是能够拖动鼠标来绘制图形,这里面实际上用到的是橡皮筋技术。在鼠标拖动中,每当鼠标的位置发生了改变,需要清除已经绘制的线段,课本已经该出了实现该过程的代码。当然之前需要在视图 View 类中添加鼠标左键按下,鼠标移动,鼠标左键抬起的消息映射。
void CShirrView::OnLButtonDown(UINT nFlags, CPoint point)
{//将鼠标左键按下位置存储到p1、p2p1 = p2 = point;b=true; //设置绘图标志pdc->SetROP2(R2_NOTXORPEN);//设置绘图模式为R2_NOTXORPEN,注意背景为白色CView::OnLButtonDown(nFlags, point);
}
void CShirrView::OnMouseMove(UINT nFlags, CPoint point)
{if (!b)return; //如果不是绘图状态,返回//P1为鼠标左键按下位置,P2为鼠标上次位置//即按前次位置重绘了一次,模式是R2_NOTXORPEN//最终效果是白色,由于底色为白,实际效果是清除了上次的线段pdc->MoveTo(p1.x,p1.y);pdc->LineTo(p2.x,p2.y);p2 = point;//p1仍为鼠标左键按下位置,P2为当前鼠标位置pdc->MoveTo(p1.x,p1.y);pdc->LineTo(p2.x,p2.y); //从P1到鼠标当前位置绘制线段CView::OnMouseMove(nFlags, point);
}
void CShirrView::OnLButtonDown(UINT nFlags, CPoint point)
{//将鼠标左键按下位置存储到p1、p2p1 = p2 = point;b=true; //设置绘图标志pdc->SetROP2(R2_NOTXORPEN);//设置绘图模式为R2_NOTXORPEN,注意背景为白色CView::OnLButtonDown(nFlags, point);
}
void CShirrView::OnMouseMove(UINT nFlags, CPoint point)
{if (!b)return; //如果不是绘图状态,返回//P1为鼠标左键按下位置,P2为鼠标上次位置//即按前次位置重绘了一次,模式是R2_NOTXORPEN//最终效果是白色,由于底色为白,实际效果是清除了上次的线段pdc->MoveTo(p1.x,p1.y);pdc->LineTo(p2.x,p2.y);p2 = point;//p1仍为鼠标左键按下位置,P2为当前鼠标位置pdc->MoveTo(p1.x,p1.y);pdc->LineTo(p2.x,p2.y); //从P1到鼠标当前位置绘制线段CView::OnMouseMove(nFlags, point);
}
上面的代码是用来画直线的,能够完成画直线的功能,那么就可以照猫画虎实现画矩形、画圆的功能了,这些图形都需要起点和终点的坐标作为画图的参数。
同时我们要明白鼠标相应这些函数是在当前视图中执行的,也就是说,我们一打开该程序,只要在视图中点击移动鼠标,这些函数其实都会相应执行到,那么我们该怎么去设计选择不同的图形?
其实这很简单,改造鼠标移动消息相应函数和鼠标左键抬起消息响应函数即可!我们可以给不同的图形一个编号,按下选择图形的按钮后,相对应的消息相应函数就会改变那个编号,鼠标移动消息相应函数和鼠标左键抬起消息响应函数根据这个编号来绘制不同的图形就可以了!
那鼠标左键按下消息响应函数不用去改造吗?
是不用改造的,因为鼠标一开始按下只是为了获取起点的坐标,而是不去画图形,所以这个对所有的图形都适用。
在这之前需要记录好每一个选择图形按键的 ID,和消息响应函数,同时在消息响应函数中完成了 CDC 对象指针 pdc 的构造。
/*
1 画直线
2 画矩形
3.画空心圆形
4.画填充矩形
5.画填充圆形
6.画圆角矩形
7.画填充圆角矩形
直线 ID_LINE,
矩形 ID_RECTANGLE
圆形 ID_CIRCLE
填充矩形 ID_TRECTANGLE
填充圆形 ID_TCIRCLE
圆角矩形 ID_YTRECTANGLE
填充圆角矩形 ID_TYTRECTANGLE
*/
void CWkfDrawingView::OnLine()
{// TODO: 在此添加命令处理程序代码MyDrawStyle = 1;pdc=new CClientDC(this);//构造对象b=false;
}void CWkfDrawingView::OnRectangle()//画矩形
{MyDrawStyle = 2;pdc=new CClientDC(this);//构造对象b=false;
}
void CWkfDrawingView::OnCircle()//画空心圆形
{MyDrawStyle = 3;pdc=new CClientDC(this);//构造对象b=false;
}
void CWkfDrawingView::OnTrectangle()
{MyDrawStyle = 4;pdc=new CClientDC(this);//构造对象b=false;
}void CWkfDrawingView::OnTcircle()
{MyDrawStyle = 5;pdc=new CClientDC(this);//构造对象b=false;
}
void CWkfDrawingView::OnYtrectangle()
{MyDrawStyle = 6;pdc=new CClientDC(this);//构造对象b=false;
}void CWkfDrawingView::OnTytrectangle()
{MyDrawStyle = 7;pdc=new CClientDC(this);//构造对象b=false;
}
下面给出鼠标按下消息相应函数、鼠标移动消息相应函数和鼠标左键抬起消息响应函数的代码,MyStart 和 MyEnd 是视图类的两个 CPoint 类型的成员变量,用来保存起点和终点的坐标。
void CWkfDrawingView::OnLButtonDown(UINT nFlags, CPoint point)//鼠标按下
{// TODO: 在此添加消息处理程序代码和/或调用默认值MyStart = MyEnd = point;pdc=new CClientDC(this);pdc->SetROP2(R2_NOTXORPEN);b = true;CView::OnLButtonDown(nFlags, point);
}
void CWkfDrawingView::OnMouseMove(UINT nFlags, CPoint point)//鼠标移动
{// TODO: 在此添加消息处理程序代码和/或调用默认值/*pdc->MoveTo(MyStart.x,MyStart.y);pdc->LineTo(MyEnd.x,MyEnd.y);*/if(!b)return ;CPen pen(GP.type, GP.width, GP.pencolor); OldPen=pdc->SelectObject(&pen);if(MyDrawStyle==1){pdc->SelectStockObject(NULL_BRUSH);pdc->MoveTo(MyStart.x,MyStart.y);pdc->LineTo(MyEnd.x,MyEnd.y);MyEnd=point;pdc->MoveTo(MyStart.x,MyStart.y);pdc->LineTo(MyEnd.x,MyEnd.y);}else if(MyDrawStyle==2){pdc->SelectStockObject(NULL_BRUSH);pdc->Rectangle(MyStart.x,MyStart.y,MyEnd.x,MyEnd.y);MyEnd = point;pdc->Rectangle(MyStart.x,MyStart.y,MyEnd.x,MyEnd.y);}else if(MyDrawStyle==3){pdc->SelectStockObject(NULL_BRUSH);pdc->Ellipse(MyStart.x,MyStart.y,MyEnd.x,MyEnd.y);MyEnd = point;pdc->Ellipse(MyStart.x,MyStart.y,MyEnd.x,MyEnd.y);}else if(MyDrawStyle==4){//pdc->SelectObject(&newBrush);CBrush bsh;bsh.CreateSolidBrush(GP.pencolor);pdc->SelectObject(&bsh);pdc->Rectangle(MyStart.x,MyStart.y,MyEnd.x,MyEnd.y);MyEnd = point;pdc->Rectangle(MyStart.x,MyStart.y,MyEnd.x,MyEnd.y);bsh.DeleteObject();}else if(MyDrawStyle==5){//pdc->SelectObject(&newBrush);CBrush bsh;bsh.CreateSolidBrush(GP.pencolor);pdc->SelectObject(&bsh);pdc->Ellipse(MyStart.x,MyStart.y,MyEnd.x,MyEnd.y);MyEnd = point;pdc->Ellipse(MyStart.x,MyStart.y,MyEnd.x,MyEnd.y);bsh.DeleteObject();}else if(MyDrawStyle==6){pdc->SelectStockObject(NULL_BRUSH);pdc->RoundRect(MyStart.x,MyStart.y,MyEnd.x,MyEnd.y,a.x,a.y);MyEnd = point;pdc->RoundRect(MyStart.x,MyStart.y,MyEnd.x,MyEnd.y,a.x,a.y);}else if(MyDrawStyle==7){CBrush bsh;bsh.CreateSolidBrush(GP.pencolor);pdc->SelectObject(&bsh);pdc->RoundRect(MyStart.x,MyStart.y,MyEnd.x,MyEnd.y,a.x,a.y);MyEnd = point;pdc->RoundRect(MyStart.x,MyStart.y,MyEnd.x,MyEnd.y,a.x,a.y);bsh.DeleteObject();}CView::OnMouseMove(nFlags, point);
}
void CWkfDrawingView::OnLButtonUp(UINT nFlags, CPoint point)//鼠标抬起
{GPen g;g.start = MyStart;g.end = MyEnd;g.width = MyWidth;g.type = type;g.style = MyDrawStyle;g.pencolor = GP.pencolor;if(MyDrawStyle==1){// TODO: 在此添加消息处理程序代码和/或调用默认值 pdc->SetROP2(R2_COPYPEN);//当前颜色覆盖背景颜色pdc->MoveTo(MyStart.x,MyStart.y);pdc->LineTo(point.x,point.y);g.c = GP.pencolor;b=false;//解除绘图关系CView::OnLButtonUp(nFlags, point);}else if(MyDrawStyle==2){pdc->SetROP2(R2_COPYPEN);pdc->Rectangle(MyStart.x,MyStart.y,MyEnd.x,MyEnd.y);g.c = GP.pencolor;b=false;//解除绘图关系CView::OnLButtonUp(nFlags, point);}else if(MyDrawStyle==3){pdc->SetROP2(R2_COPYPEN);pdc->Ellipse(MyStart.x,MyStart.y,MyEnd.x,MyEnd.y);g.c = GP.pencolor;b=false;//解除绘图关系CView::OnLButtonUp(nFlags, point);}else if(MyDrawStyle==4){//pdc->SelectObject(&newBrush);CBrush bsh;bsh.CreateSolidBrush(GP.pencolor);pdc->SetROP2(R2_COPYPEN);pdc->SelectObject(&bsh);pdc->Rectangle(MyStart.x,MyStart.y,MyEnd.x,MyEnd.y);g.c =GP.pencolor;b=false;//解除绘图关系CView::OnLButtonUp(nFlags, point);}else if(MyDrawStyle==5){//pdc->SelectObject(&newBrush);CBrush bsh;bsh.CreateSolidBrush(GP.pencolor);pdc->SetROP2(R2_COPYPEN);pdc->SelectObject(&bsh);pdc->Ellipse(MyStart.x,MyStart.y,MyEnd.x,MyEnd.y);g.c =GP.pencolor;b=false;//解除绘图关系CView::OnLButtonUp(nFlags, point);}else if(MyDrawStyle==6){pdc->SetROP2(R2_COPYPEN);pdc->RoundRect(MyStart.x,MyStart.y,MyEnd.x,MyEnd.y,a.x,a.y);g.angle=a;g.c = GP.pencolor;b=false;//解除绘图关系CView::OnLButtonUp(nFlags, point);}else if(MyDrawStyle==7){//pdc->SelectObject(&newBrush);CBrush bsh;bsh.CreateSolidBrush(GP.pencolor);pdc->SetROP2(R2_COPYPEN);pdc->SelectObject(&bsh);pdc->RoundRect(MyStart.x,MyStart.y,MyEnd.x,MyEnd.y,a.x,a.y);g.angle=a;g.c = GP.pencolor;b=false;//解除绘图关系CView::OnLButtonUp(nFlags, point);}GetDocument()->Mylist.AddTail(g);//保存信息Invalidate();
}
上面的代码中也嵌入了画笔和画刷的内容,画笔和画刷有一个特性就是一旦被定义和创建后,之后所绘制的图形就会之间用上了,所以要注意画笔和画刷的使用,而填充图形和空心图形的区别就是有没有画刷。
CBrush bsh;//定义画刷
bsh.CreateSolidBrush(GP.pencolor);//创建画刷
pdc->SelectObject(&bsh);//选择画刷
画笔的使用
CPen pen(GP.type, GP.width, GP.pencolor);//画笔的定义和创建,三个参数画笔的类型、画笔宽度、画笔的颜色
pdc->SelectObject(&pen);//选择画笔
上面还有一些代码是关于图形保存和重绘的,之后进行说明。
四、文件保存和读取——文档串行化
文档类中中提供了文档串行化(Serialize)函数能够将对象当前的状态由成员变量的值表示写入硬盘中,下次再从硬盘中读取对象的状态,从而重建对象。
但在这里的对象是什么呢?
是图形,可是图形的种类很多,如果将每一个图形的信息都通过结构体定义出来,那么会有很多的结构体来表示不同的图形,这里我选择了一种方法,将所有图形的参数,不管是特有的参数还是共有的参数,都统统封装到一个结构体中,为这个结构体创建链表,修改串行化函数就可以了!
//为了让视图和文档都认识GPen这个存储图片信息的结构体,需要在Stdafx.h中添加代码
struct GPen//保存画笔参数全局变量
{
int type;//画笔类型
int width;//画笔宽度
COLORREF pencolor;//画笔颜色
COLORREF c;
CPoint start,end;//直线、矩形和椭圆的起始点
int style;//图形的类型
CPoint angle;//圆角矩形角度
};
为文档 Doc 类添加 Gpen 的链表:
CList <GPen,GPen> Mylist;
文档类的串行化 Serialize 函数:
void CWkfDrawingDoc::Serialize(CArchive& ar)
{int i;if (ar.IsStoring())//保存{// TODO: 在此添加存储代码ar<<Mylist.GetCount();GPen g;POSITION pos = Mylist.GetHeadPosition();for(i = 0; i<Mylist.GetCount(); i++){g = Mylist.GetNext(pos);ar<<g.type<<g.width<<g.pencolor<<g.c<<g.start<<g.end<<g.style<<g.angle;}}else//读取{// TODO: 在此添加加载代码int count;ar>>count;GPen g;POSITION pos = Mylist.GetHeadPosition();for(i = 0; i<count; i++){ar>>g.type>>g.width>>g.pencolor>>g.c>>g.start>>g.end>>g.style>>g.angle;Mylist.AddTail(g);}}
}
打开之前保存文件需要有一个重绘函数,我们之前画图都只是在鼠标移动和鼠标左键抬起的时候画图,现在画图都要在视图类中的 OnDraw 中重绘了,这也就是之前的鼠标左键抬起消息响应函数中,最后需要将所画的图形信息保存到链表中的原因了。(鼠标抬起了,这个图形才真正被画出来)
GetDocument()->Mylist.AddTail(g);//保存信息
Invalidate();
之后的哪一行代码就是刷新,去执行 OnDraw 函数了。
void CWkfDrawingView::OnDraw(CDC* pDC)//加载文件重绘函数
{int i;CWkfDrawingDoc* pDoc = GetDocument();pdc=new CClientDC(this);ASSERT_VALID(pDoc);if (!pDoc)return;GPen g;POSITION pos = pDoc->Mylist.GetHeadPosition();for(i = 0; i<pDoc -> Mylist.GetCount(); i++){g = pDoc -> Mylist.GetNext(pos);CPen p(g.type,g.width,g.pencolor);pdc->SelectObject(&p);pdc->MoveTo(g.start.x,g.start.y);if(g.style==1)//画直线{pdc->SelectStockObject(NULL_BRUSH);pdc->LineTo(g.end.x,g.end.y);}if(g.style==2)//画矩形{pdc->SelectStockObject(NULL_BRUSH);pdc->Rectangle(g.start.x,g.start.y,g.end.x,g.end.y);}if(g.style==3)//画圆形{pdc->SelectStockObject(NULL_BRUSH);pdc->Ellipse(g.start.x,g.start.y,g.end.x,g.end.y);}if(g.style==4)//画填充矩形{CBrush bsh;bsh.CreateSolidBrush(g.pencolor);pdc->SelectObject(&bsh);pdc->Rectangle(g.start.x,g.start.y,g.end.x,g.end.y);bsh.DeleteObject();}if(g.style==5)//画填充圆形{CBrush bsh;bsh.CreateSolidBrush(g.pencolor);pdc->SelectObject(&bsh);pdc->Ellipse(g.start.x,g.start.y,g.end.x,g.end.y);bsh.DeleteObject();}if(g.style==6)//画圆角矩形{pdc->SelectStockObject(NULL_BRUSH);pdc->RoundRect(g.start.x,g.start.y,g.end.x,g.end.y,g.angle.x,g.angle.y);}if(g.style==7)//画填充圆角矩形{CBrush bsh;bsh.CreateSolidBrush(g.pencolor);pdc->SelectObject(&bsh);pdc->RoundRect(g.start.x,g.start.y,g.end.x,g.end.y,g.angle.x,g.angle.y);bsh.DeleteObject();}pdc->SelectObject(OldPen);}
}
五、几个对话框
颜色对话框和字体对话框是系统给的,我这里给出其按键的消息响应函数。其中的 MyFont 和是 Pcolor 是视图类的两个成员变量 CFont MyFont COLORREF Pcolor
void CWkfDrawingView::OnFont()//字体设置
{CFontDialog dlg;if(IDOK==dlg.DoModal()){if(MyFont.m_hObject){MyFont.DeleteObject();}MyFont.CreateFontIndirect(dlg.m_cf.lpLogFont);//字体信息MyFontName=dlg.m_cf.lpLogFont->lfFaceName;//字体的名称}
}
void CWkfDrawingView::OnPancolor()//画笔颜色设置
{CColorDialog dlg(0,CC_FULLOPEN);if(dlg.DoModal()){Pcolor = dlg.GetColor();//从颜色对话框中获取颜色信息GP.pencolor=Pcolor;}else if(dlg.DoModal()==IDCANCEL){}
}
对于自定义的对话框,有画笔的宽度设置,画笔类型设置,文本输入,代码如下:
void CWkfDrawingView::OnTxt()//文本输入
{// TODO: 在此添加命令处理程序代码CTxtlog dlg;if(dlg.DoModal()==IDOK){int X=dlg.MyX;int Y=dlg.MyY;CString String=dlg.MyString;pdc=new CClientDC(this);//构造对象pdc->SetTextColor(GP.pencolor);//设置文件颜色pdc->SelectObject(&MyFont);pdc->TextOut(X,Y,String);}else if(dlg.DoModal()==IDCANCEL){}
}void CWkfDrawingView::OnLineW()//画笔宽度
{// TODO: 在此添加命令处理程序代码CLWidth dlg;if(dlg.DoModal()==IDOK){GP.width=dlg.width;//更新画笔的宽度MyWidth=dlg.width;}else if(dlg.DoModal()==IDCANCEL){}
}
/*
PS_SOLID 实线
PS_DASH 虚线
PS_DOT 点线
PS_DASHDOT 点化线
PS_DASHDOTDOT 双点化线
*/void CWkfDrawingView::OnSolid()//线条类型
{// TODO: 在此添加命令处理程序代码type=PS_SOLID;GP.type=type;pdc=new CClientDC(this);
}
void CWkfDrawingView::OnDash()
{// TODO: 在此添加命令处理程序代码type=PS_DASH;GP.type=type;
}
void CWkfDrawingView::OnDot()
{// TODO: 在此添加命令处理程序代码type=PS_DOT;GP.type=type;
}void CWkfDrawingView::OnDashdot()
{// TODO: 在此添加命令处理程序代码type=PS_DASHDOT;GP.type=type;
}void CWkfDrawingView::OnDashdotdot()
{// TODO: 在此添加命令处理程序代码type=PS_DASHDOTDOT;GP.type=type;
}
但对于文本输入和字体宽度设置,需要从对话框中获取信息,保存到变量中,这就需要交换函数,在这之前需要将自定义的对话框设置一个对话框类,与对话框资源相关联,所有的代码处理都在对话框类中进行。以文本输入为例,添加文本输入对话框类
# pragma once
// CTxtlog 对话框
class CTxtlog : public CDialog
{
DECLARE_DYNAMIC(CTxtlog)public:
CTxtlog(CWnd* pParent = NULL); // 标准构造函数
virtual ~CTxtlog();// 对话框数据
enum { IDD = IDD_TEXT };protected:
virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV 支持DECLARE_MESSAGE_MAP()
public:
int MyX;
public:
int MyY;
public:
CString MyString;
};
修改其中的数据交换函数为:
void CTxtlog::DoDataExchange(CDataExchange* pDX)
{CDialog::DoDataExchange(pDX);DDX_Text(pDX, ID_TXTX, MyX);DDX_Text(pDX, ID_TXTY, MyY);DDX_Text(pDX, ID_TXTS, MyString);
}
相关文章:
基于C++(MFC)图形编辑界面工具
MFC 图形编辑界面工具 一、背景 喔,五天的实训终于结束了,学校安排的这次实训课名称叫高级程序设计实训,但在我看来,主要是学习了 Visual C .NET 所提供的 MFC(Microsoft Foundation Class)库所提供的类及其功能函数…...
Linux网络通信核心机制解析与层级架构探秘
作为现代操作系统的神经网络,Linux网络通信系统通过多层协作架构实现高效的数据传输机制。本文将从模块化设计、报文处理路径、核心组件交互等多个维度,解析Linux网络通信系统的实现原理与优化策略,并结合内核源码示例与性能调优实践…...
Tensorflow实现用接口调用模型训练和停止训练功能
语言:Python 框架:Flask、Tensorflow 功能描述:存在两个接口,一个接口实现开始训练模型的功能,一个接口实现停止训练的功能。 实现:用一个全局变量存储在训练中的模型。 # 存储所有训练任务 training_task…...
HTTP测试智能化升级:动态变量管理实战与效能跃迁
在Web应用、API接口测试等领域,测试场景的动态性和复杂性对测试数据的灵活管理提出了极高要求。传统的静态测试数据难以满足多用户并发、参数化请求及响应内容验证等需求。例如,在电商系统性能测试中,若无法动态生成用户ID、订单号或实时提取…...
PyTorch 浮点数精度全景:从 float16/bfloat16 到 float64 及混合精度实战
PyTorch 在深度学习中提供了多种 IEEE 754 二进制浮点格式的支持,包括半精度(float16)、Brain‑float(bfloat16)、单精度(float32)和双精度(float64),并通过统…...
《软件设计师》复习笔记(14.2)——统一建模语言UML、事务关系图
目录 1. UML概述 2. UML构造块 (1) 事物(Things) (2) 关系(Relationships) 真题示例: 3. UML图分类 (1) 结构图(静态) (2) 行为图(动态) 4. 核心UML图详解 5.…...
基于C++(MFC)的细胞识别程序
基于 mfc 的细胞识别程序 一、图像处理课程设计目标 1.1 课题与技术指标 课题:利用图像处理技术设计细胞识别程序。 技术指标: 实验 VS2019 MFC 开发平台待识别图像为 24bit 的真彩色细胞图像进行处理要求识别出细胞,并且保证准确度情况下…...
【前端HTML生成二维码——MQ】
提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 前端HTML生成二维码——MQ 前言本文将介绍前端HTML如何更具用户输入的内容生成对应的二维码,附页面代码、实现函数、js脚本。一、自定义显示页面1、效果图二、使用步骤1、引入库2、实现函数3、页面及函数代…...
Spring Boot自动配置原理深度解析:从条件注解到spring.factories
大家好!今天我们来深入探讨Spring Boot最神奇的特性之一——自动配置(Auto-configuration)。这个功能让Spring Boot如此受欢迎,因为它大大简化了我们的开发工作。让我们一起来揭开它的神秘面纱吧!👀 🌟 什么是自动配置…...
【unity实战】Animator启用root motion根运动动画,实现完美的动画动作匹配
文章目录 前言1、动画分类2、如何使用根位移动画? 一、根位移动画的具体使用1、导入人形模型2、导入动画3、配置动画参数4、配置角色Animator动画状态机5、使用代码控制人物前进后退 二、问题分析三、Humanoid动画中的Root Motion机制及相关配置1、Humanoid动画中的…...
(51单片机)LCD显示数据存储(DS1302时钟模块教学)(LCD1602教程)(独立按键教程)(延时函数教程)(I2C总线认识)(AT24C02认识)
目录 演示视频: 源代码 main.c LCD1602.c LCD1602.h AT24C02.c AT24C02.h Key.c Key.h I2C.c I2C.h Delay.c Delay.h 代码解析与教程: Dealy模块 LCD1602模块 Key模块 I2C总线模块 AT24C02模块 /E2PROM模块 main模块 演示视频: &…...
2d深度预测
Depth anything v1 相对深度估计,要用绝对深度估计需要微调 概要: 1 使用大量的未标注图像信息 2 采用优化策略—数据增强工具(作用在未标注图像) 3 进行辅助监督—继承语义分割知识(作用在未标注图像) 数据层面: …...
Android12 ServiceManager::addService源码解读
源码 Status ServiceManager::addService(const std::string& name, const sp<IBinder>& binder, bool allowIsolated, int32_t dumpPriority) {auto ctx mAccess->getCallingContext();// apps cannot add servicesif (multiuser_get_app_id(ctx.uid) >…...
【HDFS入门】HDFS性能调优实战:从基准测试到优化策略
目录 引言 1 HDFS性能评估体系 1.1 性能评估体系架构 1.2 基准测试工具对比 2 TestDFSIO基准测试实战 2.1 TestDFSIO工作原理 2.2 测试执行步骤 2.3 结果分析指标 3 TeraSort基准测试实战 3.1 TeraSort测试流程 3.2 测试执行命令 3.3 关键性能指标 4 HDFS性能调优…...
Linux 内核开发/测试工具对比 Windows 驱动验证工具 (Driver Verifier)
Windows 的 Driver Verifier 是一个用于测试和验证驱动程序的强大工具。在 Linux 内核开发中,虽然没有一个完全等价的单一工具,但有多种工具和框架可以提供类似或更专业的功能。 Linux 内核开发和测试工具 1. KASAN (Kernel Address Sanitizer) 功能&…...
通信算法之269 : OFDM信号的循环自相关特性用于无人机图传信号识别
OFDM信号的循环自相关特性是其循环平稳性的核心体现,如下: [相关仿真代码,联系,提供] 一、循环自相关特性来源 循环前缀引入周期性 OFDM符号通过添加循环前缀(CP)形成符号周期结构,导致信号具有循环平稳性26。每个符号的CP与尾部数据重复,在时延等于FFT长度(N…...
【无人机】电子速度控制器 (ESC) 驱动电机,常见的电调协议,PWM协议,Oneshot协议,DShot协议
目录 1、ESCs & 电机 #1.1、ESC 协议 --ESC Protocols #1.1.1、PWM协议,需要校准 #1.1.2、One Shot 125协议,速率更快 #1.1.3、DShot ,减少延迟,无需校准 #1.1.4、无人机CAN 2、PWM 伺服系统和 ESC(电机控…...
LeadeRobot具身智能应用标杆:无人机X柔韧具身智能,空中精准作业游刃有余
当前,具身智能已成为全球科技领域的前沿焦点,更受到国家战略级重视,吸引科技产业巨头抢滩布局。但同时,具身智能的商业化路径、规模化应用场景、技术成本等难题也开始在资本界与产业圈引起广泛讨论。 目前,万勋科技基于Pliabot 柔韧技术已推出多款具身智能柔韧机器人产品,在柔…...
WebSocket:实现实时双向通信的技术
WebSocket是一种网络通信协议,它在单个TCP连接上提供全双工通信。WebSocket协议在2011年被IETF(互联网工程任务组)标准化为RFC 6455,并由W3C(万维网联盟)制定了WebSocket API标准,使得客户端&am…...
探索 HumanoidBench:类人机器人学习的新平台
在科技飞速发展的当下,类人机器人逐渐走进我们的视野,它们有着和人类相似的外形,看起来能像人类一样在各种环境里完成复杂任务,潜力巨大。但实际上,让类人机器人真正发挥出实力,还面临着重重挑战。 这篇文…...
「数据可视化 D3系列」入门第十一章:力导向图深度解析与实现
D3.js 力导向图深度解析与实现 力导向图核心概念 力导向图是一种通过物理模拟来展示复杂关系网络的图表类型,特别适合表现社交网络、知识图谱、系统拓扑等关系型数据。其核心原理是通过模拟粒子间的物理作用力(电荷斥力、弹簧引力等)自动计…...
「数据可视化 D3系列」入门第八章:动画效果详解(让图表动起来)
动画效果详解 一、D3.js动画核心API1. d3.transition()2. transition.duration()3. transition.delay()4. 其他重要API 二、动画实现原理三、完整动画示例解析1. 柱状图生长动画2. 文本跟随动画 四、动画效果优化技巧1. 缓动函数选择:2. 组合动画:3. 动画…...
index: 自动化浏览器智能体
GitHub:https://github.com/lmnr-ai/index 更多AI开源软件:https://www.aiinn.cn/ 在做浏览器自动化脚本时,我们常常需要编写大量代码来处理复杂的网页交互,不仅耗时耗力,还难以调试和优化,要是出错更是难以…...
网页端调用本地应用打开本地文件(PDF、Word、excel、PPT)
一、背景原因 根据浏览器的安全策略,在网页端无法直接打开本地文件,所以需要开发者曲线救国。 二、实现步骤 前期准备: 确保已安装好可以打开文件的应用软件,如,WPS; 把要打开的文件统一放在一个文件夹&am…...
如何批量在多个 Word 文档末尾添加广告页面
Word是我们日常使用非常频繁的文档编辑软件,凭借其强大的文本处理功能,如文字输入、格式设置、段落排版、图片插入等,可以帮助我们轻松创建专业且美观的文档。不过呢当我们需要将这些文档分享给他人时,往往需要在每个文档的末尾添…...
JavaScript原生实现简单虚拟列表
旧笔记,最近使用时做了点新优化,之前只发在了个人博客上 地址:JavaScript原生实现简单虚拟列表 背景 在公司项目中,需要给商品配置大量的属性值,可能其中一个属性的值数量就有成百上千条。 一个商品会有很多属性&…...
安心联车辆管理平台应用前景分析
安心联车辆管理平台凭借其技术创新与行业适配能力,展现出广阔的应用前景。以下从技术驱动、行业覆盖、实际效益、市场策略及未来潜力五个维度进行分析: 一、技术驱动的核心竞争力 高精度定位与多传感器融合 安心联采用北斗/GPS双模定位技术,实…...
力扣每日打卡 2176. 统计数组中相等且可以被整除的数对(简单)
力扣 2176. 统计数组中相等且可以被整除的数对 简单 前言一、题目内容二、解题方法1. 暴力解法2.官方题解官方也是暴力解法 前言 这是刷算法题的第十三天,用到的语言是JS 题目:力扣 2176. 统计数组中相等且可以被整除的数对(简单) 一、题目内容 给你一…...
OpenStack Yoga版安装笔记(22)Swift笔记20250418
一、官方文档 https://docs.openstack.org/swift/yoga/admin/objectstorage-components.html#https://docs.openstack.org/swift/yoga/admin/objectstorage-components.html# 二、对象存储简介(Introduction to Object Storage) OpenStack 对象存储&a…...
Linux 线程互斥
目录 Linux线程互斥 进程线程间的互斥相关背景概念 互斥量的接口 初始化互斥量 互斥量加锁和解锁 锁的封装 编辑 互斥量加锁的非阻塞版本 互斥量实现原理探究 可重入VS线程安全 概念 常见的线程不安全的情况 常见的线程安全的情况 常见的不可重入的情况 常见的…...
webgl入门实例-12WebGL 投影矩阵 (Projection Matrix)基本概念
WebGL 投影矩阵 (Projection Matrix) 投影矩阵是将3D场景从视图空间(相机空间)转换到裁剪空间的关键矩阵,它决定了3D物体如何投影到2D屏幕上。 投影矩阵的作用 定义3D空间到2D平面的映射方式确定视景体(view frustum)的形状和范围实现透视效果(近大远小)或正交投…...
《实战AI智能体》——邮件转工单的AI自动化
💡 为什么使用 邮件转工单(AI自动化)模板 想象一下,您的邮箱像是一个繁忙的宇宙港口,每封邮件都是一艘载着信息的飞船。当这些飞船抵达时,我们的自动化系统就像是一位高效的宇宙调度员,迅速将每艘飞船(邮件)转化为一艘艘探索号(工单),并为它们分配勇敢的宇航员(…...
YOLOv8 Bug 及解决方案汇总 【2024.1.24更新】【环境安装】【训练 断点续训】OMPError / KeyError
YOLOv8 Bug 及解决方案汇总:深入解析与应对 引言 YOLOv8作为一款高性能的目标检测算法,在实际应用中难免会遇到各种各样的问题。本文将对YOLOv8常见的Bug进行汇总,并提供相应的解决方案,旨在帮助开发者更好地使用和优化YOLOv8。…...
健康养生:开启活力生活新篇章
在当代社会,熬夜加班、久坐不动、外卖快餐成为许多人的生活常态,随之而来的是各种亚健康问题。想要摆脱身体的疲惫与不适,健康养生迫在眉睫,它是重获活力、拥抱美好生活的关键。 应对不良饮食习惯带来的健康隐患,饮…...
Yocto项目实战教程 · 第4章:4.3小节-层
🔍 B站相应的视频教程: 📌 Yocto项目实战教程-第4章-4.3小节-层 记得三连,标为原始粉丝。 在 Yocto 项目中,元数据(Metadata)不仅是构建系统的核心驱动力,更是实现高度定制化、可移植…...
小红书爬虫,小红书api,小红书数据挖掘
背景: 小红书(Xiaohongshu)是一款结合社交、购物和内容分享的移动应用,近年来在中国以及全球范围内拥有大量的用户群体。小红书上的内容包括用户的消费体验、生活方式、旅行分享、时尚搭配等。通过这些内容,用户可以了…...
选择 iOS 按键精灵无根有根越狱辅助工具的理由
节省成本 使用 iOS 按键精灵,每台设备可为你减少 5 - 10 元的签名成本。对于需要使用大量脚本或者多设备操作的用户来说,长期下来能够节省一笔可观的费用。 不断更新优化 按键精灵团队始终致力于产品的更新与优化。新版本不断增加新功能&#x…...
电脑里的AI帮手:Open Interpreter智能助手食用指南
Open Interpreter简介如下: interpreter是一个使用python开发的命令行工具,可以让你在终端中使用类似AI对话的方式,只需简单输入指令要求,即可自动编写程序、执行代码,实现各种自动化操作interpreter有自动检测输出结…...
Windows软件界面分析软件-控件识别工具
Inspect.exe 这是微软提供的一款 UI 自动化检查工具,主要用于开发和测试应用程序的辅助功能(Accessibility)及 UI 自动化。 主要功能: 查看界面元素的属性(如名称、角色、状态、位置等)。 支持 UIA&…...
iOS 冷启动时间监控:启动起点有哪些选择?
⏱️ iOS 冷启动时间监控:启动起点有哪些选择? 作者:侯仕奇 来源:sqi.io 在监控 iOS 冷启动性能时,一个关键问题是:如何精确记录 App 冷启动的开始时间? 本文将对不同的“冷启动起点”监控方式…...
MacOS怎么显示隐藏文件
现象描述: 有些文件比如git的配置文件会作为隐藏文件存在。 Mac os默认是不显示隐藏文件的。 但是很多场合下我们需要查看或者编辑这些隐藏文件。 解决方法: 如下图所示,在Finder中使用Shift⇧Command⌘.快捷键 显示和隐藏都是同样的按…...
苹果紧急修复两个已被利用的iOS漏洞,用于针对特定目标的复杂攻击
苹果公司已发布iOS 18.4.1和iPadOS 18.4.1更新,修复两个被用于针对特定iPhone用户实施高度定向、复杂攻击的关键零日漏洞。 这两个漏洞存在于CoreAudio和RPAC组件中,攻击者可利用它们在受影响设备上执行任意代码或绕过安全保护机制。 两个正被活跃利用…...
8.观察者模式:思考与解读
原文地址:观察者模式:思考与解读 更多内容请关注:7.深入思考与解读设计模式 引言 在开发软件时,系统的某些状态可能会发生变化,而你希望这些变化能够自动通知到依赖它们的其他模块。你是否曾经遇到过,系统中某个对象…...
13.编码器的结构
从入门AI到手写Transformer-13.编码器的结构 13.编码器的结构代码 整理自视频 老袁不说话 。 13.编码器的结构 T r a n s f o r m e r E n c o d e r : 输入 [ b , n ] TransformerEncoder:输入[b,n] TransformerEncoder:输入[b,n] E m b e d d i n g : − > [ b , n , d ]…...
java 设计模式之模板方法模式
简介 模板方法模式:定义一个算法的基本流程,将一些步骤延迟到子类中实现。模板方法模式可以提高代码的复用性, 模板方法中包含的角色: 抽象类:负责给出一个算法的基本流程,它由一个模板方法和若干个基本…...
C++面向对象
面向对象的思想 面向过程: 根据程序的执行过程,来设计软件的所有细节。面向过程的缺点:开发大型项目时,越来越难以把控,甚至失去控制。后期维护、更新成本很大。解决方案:使用面向对象。 什么是面向对象…...
守护进程编程
目录 一、守护进程 1.1 守护进程概述 1.2 守护进程的功能及特点 1.2.1 守护进程的功能 1.2.2 守护进程的特点 1.3 主要过程 1.4 阿里云服务器编程实现守护进程 1.4.1 daemon 命令 1.4.2 nohup命令 1.4.3 fork()编程实现 1.5 在树莓派中通过三种方式创建守护进程 1.5…...
【Spring Boot 源码学习】深入 ConfigurableEnvironment 的初始化过程
《Spring Boot 源码学习系列》 深入 ConfigurableEnvironment 的初始化过程 一、引言二、配置环境的初始化2.1 源码总览2.2 prepareEnvironment 方法2.2.1 获取或创建可配置环境2.2.2 配置环境并设置参数2.2.3 将配置属性源附加到环境中2.2.4 触发环境准备事件2.2.5 将DefaultP…...
若依集成BladeX单点登录的令牌管理与api请求流程
目录 概述系统架构单点登录流程令牌管理机制接口调用流程关键代码实现数据结构安全性考虑常见问题与解决 概述 本文档详细说明若依系统如何实现与BladeX的单点登录集成,包括令牌管理和接口调用的完整流程。整个集成采用基于OAuth2的授权码流程,允许用…...
54常用控件_QLCDNumber的属性
目录 代码示例: 倒计时 QLCDNumer 是一个专门用来显示数字的控件.类似于“老式计算器”的效果 核心属性 属性 说明 intValue QLCDNumber显示的数字值(int). value QLCDNumber 显示的数字值(double). 和intValue是联动的. 例如给value设为1.5, intValue的值就是2. 另外&a…...