wxWidget入门(九)

这一期做一个简易画板

带有功能:

  1. 画直线
  2. Undo
  3. Redo
  4. 保存图片

回顾上一期的内容,画画是用鼠标拖动时间,不断追踪鼠标位置,进而画出断续的点,缺点明显。

先把上一期问题解决,解决思路:利用短直线将上一次获取的鼠标位置,和这一次连接起来。

①画出直线

void MyFrame::OnMotion(wxMouseEvent &event)
{
    static int count;
    wxMemoryDC dc;            //设备上下文dc
    if(event.Dragging())      //如果发生拖动事件
    {
	    wxPen pen(*wxRED, 1); //设置画笔
	    dc.SetPen(pen);
		
	    if(count)             //第一个点不做处理,只是将lastPoint记录(保存上一点位置)
	    {
		    dc.DrawLine(event.GetPosition(), lastPoint);//连接两点
	    }
	    lastPoint = event.GetPosition();
	    count++;
    }
    else                      //拖动结束
    {
        count = 0;	
    }
}

②撤销画线

我们这里做的是可以撤销不限次数,只要内存足够大。

因为撤销,所以需要记录每一次画线之前的图像。

还有一点需要注意的是,画一条连续直线,OnMotion函数并不是只进入一次,

可以这么理解:一条连续直线,需要n条短直线连接而成。而OnMotion函数就是实现画短直线的功能,即需要进入OnMotion函数n次。

思路:在myframe类中添加两个wxBitmap成员,lastmap和buffer_img

lastmap是存储画线之前的图像。

buffer_img是当前实时更新的图像。

还有需要链表存储lastmap 

我这里是写了一个类 记录画的线(短直线)和 上一次的图像

class myline
{
public:
	myline(wxBitmap map, wxPoint p1, wxPoint p2)
	{
		lastmap = map;
		this->p1 = p1;
		this->p2 = p2;
	}
	wxBitmap lastmap;
	wxPoint p1;
	wxPoint p2;
};

链表需要双层,内层是:一条直线所需要构成的n条短直线,外层是:你画的m条直线。

list<list<myline>>  allline;

更新之后的OnMotion如下图: 

void MyFrame::OnMotion(wxMouseEvent &event)
{
	static int count; //用作lastPoint的标志位    0:仅仅赋值
	wxMemoryDC dc;
	static int flag;  //用作记录lastline的标志位  0:记录

	if(event.Dragging())
	{
		flag = 0;
		wxPen pen(*wxRED, 1);
	
		dc.SetPen(pen);
		dc.SelectObject(buffer_img); //设置实时更新的对象
		
		if(count) 
		{
			dc.DrawLine(event.GetPosition(), lastPoint);
			
			myline line1 = myline(lastmap, event.GetPosition(), lastPoint);

//list<myline> lastline是成员变量//存储当前直线的段直线			
			lastline.push_back(line1);
            
		}
		lastPoint = event.GetPosition();
		count++;
		dc.SetPen(wxNullPen);
		dc.SelectObject(wxNullBitmap);

		this->Refresh(false);//刷新界面 进入OnPaint函数
	}
	else//不画线的时候 仅仅移动鼠标也会进入
	{
//在鼠标结束拖拉(停止画线)瞬间(每次,仅一次)将当前图像设置为下次画线的上次图像
		if(!flag)
		{
			lastmap = buffer_img;
		}
		if(lastline.size()) //如果有画线
		{
			allline.push_back(lastline);//记录当前画的直线

			lastline.clear();
		}
		count = 0;
		flag = 1;
	}
}

void MyFrame::OnPaint(wxPaintEvent &event)
{
	dc.SetBackground(wxBrush(*wxWHITE_BRUSH));
	dc.Clear();                       //刷新背景
	PrepareDC(dc);                    //wx机制 准备设备
	dc.DrawBitmap(buffer_img,0,0);    //画图像

}

我们已经记录了画的直线,那么接下来处理Undo撤销事件

//界面中添加菜单栏 file 在其中添加Undo功能,设置ctrl-z快捷键
wxMenu *fileMenu = new wxMenu;
fileMenu->Append(wxID_UNDO, wxT("&Undo\tCtrl-z"));

//连接事件
Connect(wxID_UNDO,wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(MyFrame::OnUndo));
void MyFrame::OnUndo(wxCommandEvent &event)
{
 	if(allline.size())//如果有画线
 	{
 		list<myline> line1 = allline.back(); //提取最新的画线
		redo_line.push_back(line1);          //添加到链表里
		wxMemoryDC dc;
		buffer_img = line1.begin()->lastmap; //提取上一幅图像

 		allline.pop_back();                  //剔除最后一个记录
		if(!allline.size())                  //如果没有直线了
			buffer_img = wxBitmap(lastmap.GetWidth(),lastmap.GetHeight());//设置空图像
		this->Refresh(false);                //刷新buffer_img
 	}
}

③Redo,与Undo类似

多了一个当前图像,在myfile中添加nowmap(wxBitmap)的成员

后面贴全部代码

④保存图像

值得注意的是不能直接savefile,需要将bitmap转成wximage保存才有用、。

bitmap.ConvertToImage().SaveFile(wxT("my.bmp"),wxBITMAP_TYPE_BMP);

最后贴出最终实现代码

#include <wx/wx.h>
#include <wx/dcbuffer.h>
#include <wx/list.h>
#include <list>
using namespace std;
class myline
{
public:
	myline(wxBitmap map, wxPoint p1, wxPoint p2)
	{
		lastmap = map;
		this->p1 = p1;
		this->p2 = p2;
	}
	wxBitmap lastmap;
	wxBitmap nowmap;
	wxPoint p1;
	wxPoint p2;
};
class MyFrame : public wxFrame
{
public:
	MyFrame(const wxString & title);
	void OnPaint(wxPaintEvent &event);
	void OnMotion(wxMouseEvent &event);
	void OnUndo(wxCommandEvent & event);
	void OnRedo(wxCommandEvent &event);
private:
	wxMenuBar *menubar;
	wxBitmap buffer_img;
	wxPoint lastPoint;
	list<list<myline>>  allline;
	list<list<myline>>	redo_line;
	list<myline> lastline;
	wxBitmap lastmap;
};
MyFrame::MyFrame(const wxString & title)
	:wxFrame(NULL, -1, title, wxDefaultPosition, wxSize(400, 300))
{

	menubar = new wxMenuBar;
	wxMenu *fileMenu = new wxMenu;
	fileMenu->Append(wxID_UNDO, wxT("&Undo\tCtrl-z"));
	fileMenu->Append(wxID_REDO, wxT("&Redo\tCtrl-y"));
	menubar->Append(fileMenu, wxT("&File"));
	SetMenuBar(menubar);
	wxClientDC dc(this);
	int w =dc.GetSize().GetX();
	lastmap.Create(dc.GetSize());
	buffer_img= wxBitmap(dc.GetSize());


	Connect(wxEVT_PAINT,wxPaintEventHandler(MyFrame::OnPaint));
	Connect(wxEVT_MOTION, wxMouseEventHandler(MyFrame::OnMotion));
	Connect(wxID_UNDO,wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(MyFrame::OnUndo));
	Connect(wxID_REDO,wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(MyFrame::OnRedo));
}
void MyFrame::OnUndo(wxCommandEvent &event)
{
 	if(allline.size())
 	{
 		list<myline> line1 = allline.back();
		redo_line.push_back(line1);//添加到链表里
		wxMemoryDC dc;
		buffer_img = line1.begin()->lastmap;
 		allline.pop_back();//剔除最后一个记录
		if(!allline.size())
			buffer_img = wxBitmap(lastmap.GetWidth(),lastmap.GetHeight());
		this->Refresh(false);
 	}
}
void MyFrame::OnRedo(wxCommandEvent &event)
{
	if(redo_line.size())
	{
		list<myline> line1 = redo_line.back();
		allline.push_back(line1);//添加到链表里
		wxMemoryDC dc;
		buffer_img = line1.begin()->nowmap;

		redo_line.pop_back();//剔除最后一个记录
		this->Refresh(false);
	}
}
void MyFrame::OnMotion(wxMouseEvent &event)
{
	static int count;
	wxMemoryDC dc;
	static int flag;

	if(event.Dragging())
	{
		flag = 0;
		wxPen pen(*wxRED, 1);
	
		dc.SetPen(pen);
		dc.SelectObject(buffer_img);
		
		if(count)
		{
			dc.DrawLine(event.GetPosition(), lastPoint);
			

			myline line1 = myline(lastmap, event.GetPosition(), lastPoint);
			
			lastline.push_back(line1);
		}
		lastPoint = event.GetPosition();
		count++;
		dc.SetPen(wxNullPen);
		dc.SelectObject(wxNullBitmap);

		this->Refresh(false);
	}
	else
	{
		if(!flag)
		{
			lastmap =buffer_img;
		}
		if(lastline.size())
		{
			lastline.begin()->nowmap = buffer_img;
			allline.push_back(lastline);

			lastline.clear();
		}
		count = 0;
		flag = 1;	
	}
}
void MyFrame::OnPaint(wxPaintEvent &event)
{
	wxBufferedPaintDC dc(this);
	dc.SetBackground(wxBrush(*wxWHITE_BRUSH));
	dc.Clear();
	PrepareDC(dc);
	dc.DrawBitmap(buffer_img,0,0);
}
class MyApp : public wxApp
{
public:
	virtual bool OnInit();
};

IMPLEMENT_APP(MyApp)

bool MyApp::OnInit()
{
	MyFrame * frame = new MyFrame(wxT("img"));
	frame->Show(true);
	
	return true;
}

如果有更好的实现方法,告诉我哈。。。

猜你喜欢

转载自blog.csdn.net/u012198575/article/details/82698413