1. 程式人生 > >wxWidget入門(九)

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;
}

如果有更好的實現方法,告訴我哈。。。