wxWidget入門(九)
阿新 • • 發佈:2018-11-11
這一期做一個簡易畫板
帶有功能:
- 畫直線
- Undo
- Redo
- 儲存圖片
回顧上一期的內容,畫畫是用滑鼠拖動時間,不斷追蹤滑鼠位置,進而畫出斷續的點,缺點明顯。
先把上一期問題解決,解決思路:利用短直線將上一次獲取的滑鼠位置,和這一次連線起來。
①畫出直線
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;
}
如果有更好的實現方法,告訴我哈。。。