这一期做一个简易画板
带有功能:
回顾上一期的内容,画画是用鼠标拖动时间,不断追踪鼠标位置,进而画出断续的点,缺点明显。
先把上一期问题解决,解决思路:利用短直线将上一次获取的鼠标位置,和这一次连接起来。
①画出直线
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> 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 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 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
#include
#include
#include
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> allline;
list> redo_line;
list 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 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 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;
}
如果有更好的实现方法,告诉我哈。。。