jueves, 8 de mayo de 2008

Simple graphics with WxWidgets and Direct Context (WxDC)

UPDATE #1: Added a more "elegantly" version of this code inside of a class & allowing Scrolling.

Today I fix & improve the source code of the post wxogl-vs-wxdc-vs-wxart2d that I wrote last week, and now the same application can add until 10 tables (rectangles with text) inside the frame, then you can select only one at time (red line in the border) or move it with drag & drop in the frame.

This demo can help people (newbies) who wants to create a modeling tool of some kind with WxWidgets, is not great thing but have good ideas that can be used for larger applications:

- Diagram it's draw at the OnPaint event of the windows, because this there isn't need of a separate thread or something like that. And CPU usage of application it's low.

- It's easy to adapt the example to use a Model View Controller, only create data structures with base information for objects inside diagram, and a DrawAll function that can read that information and draw it, then Controller syncrhonize
all changes between the data and the image.

- Flicker have been eliminated.

- Don't use any complex third party library, this means no highly optimized rendering neither undo feature (by default) or complex document hierarchy, but for me best way of programa something always should follow this rule: "Simpler is better "

Source code:


Test.cpp
  1. #include "wx/wxprec.h"

  2. #define MAXRECTANGLES 10

  3. #ifdef __BORLANDC__

  4. #pragma hdrstop

  5. #endif


  6. #ifndef WX_PRECOMP


  7. #endif


  8. #include "wx/wx.h"

  9. #include "wx/dcbuffer.h"


  10. enum MenuItems

  11. {

  12. ID_Add = 2,


  13. ID_Quit = 1,

  14. ID_About = 0,


  15. };


  16. class MyApp: public wxApp

  17. {

  18. virtual bool OnInit();


  19. };



  20. IMPLEMENT_APP(MyApp)



  21. class MyCanvas; //Just a prototype


  22. class MyFrame: public wxFrame


  23. {

  24. public:


  25. MyFrame(const wxString& title,

  26. const wxPoint& pos, const wxSize& size);



  27. //Menu Events

  28. void OnQuit(wxCommandEvent& event);


  29. void OnAbout(wxCommandEvent& event);

  30. void OnAddRectangle(wxCommandEvent& event);



  31. //Global Class Variables

  32. MyCanvas *paintArea;

  33. DECLARE_EVENT_TABLE()


  34. };


  35. class MyCanvas: public wxScrolledWindow

  36. {

  37. public:

  38. MyCanvas::MyCanvas(MyFrame *parent, wxSize size);


  39. //MyCanvas Functions

  40. void MyCanvas::DrawAll(wxBufferedDC& bdc);


  41. int MyCanvas::GetIndexSelected();

  42. //MyCanvas Events


  43. void MyCanvas::OnPaint(wxPaintEvent& event);

  44. void MyCanvas::OnMotion(wxMouseEvent& event);


  45. void MyCanvas::OnErase(wxEraseEvent& event);

  46. void MyCanvas::OnEraseBackGround(wxEraseEvent& event);


  47. //Global Class Variables

  48. int rts[MAXRECTANGLES][2];


  49. int rts_Size;

  50. int pressed,posx,posy,selected;

  51. wxSize c_size;



  52. private:

  53. MyFrame *m_owner;

  54. DECLARE_EVENT_TABLE()


  55. };




  56. //**********************Begin implementations

  57. bool MyApp::OnInit()

  58. {

  59. MyFrame *frame = new MyFrame( wxT("Hello World Drawing in 2D"),


  60. wxPoint(50,50), wxSize(800,600) );


  61. frame->Show(TRUE);


  62. SetTopWindow(frame);

  63. //frame->SetScrollbar(wxVERTICAL,0,16,50);



  64. //initialize values

  65. frame->paintArea->rts_Size=0;


  66. for(int i=0;i<MAXRECTANGLES;i++){

  67. frame->paintArea->rts[i][0]=-1;


  68. frame->paintArea->rts[i][1]=-1;


  69. }

  70. frame->paintArea->pressed=-1; //No rectangle selected


  71. return TRUE;

  72. }



  73. //MyFrame Constructor

  74. MyFrame::MyFrame(const wxString& title,


  75. const wxPoint& pos, const wxSize& size)

  76. : wxFrame((wxFrame *)NULL, -1, title, pos, size)


  77. {

  78. wxMenu *menuFile = new wxMenu;

  79. menuFile->Append( ID_Add, wxT("&Add new Rectangle..." ));


  80. menuFile->Append( ID_About, wxT("A&bout..." ));

  81. menuFile->AppendSeparator();


  82. menuFile->Append( ID_Quit, wxT("E&xit" ));


  83. wxMenuBar *menuBar = new wxMenuBar;


  84. menuBar->Append( menuFile, wxT("&File") );


  85. SetMenuBar( menuBar );



  86. CreateStatusBar();

  87. SetStatusText(wxT("Proof of Concept drawing") );



  88. paintArea = new MyCanvas(this,wxSize(800,600));

  89. paintArea->SetScrollbars( 10, 10, 100, 240 );




  90. }



  91. //MyCanvas Constructor

  92. MyCanvas::MyCanvas(MyFrame *parent, wxSize size)


  93. : wxScrolledWindow(parent, wxID_ANY, wxDefaultPosition, size,

  94. wxHSCROLL | wxVSCROLL | wxBORDER | wxRETAINED)


  95. {

  96. m_owner = parent;

  97. c_size = size;


  98. }


  99. //Events Table Macros

  100. BEGIN_EVENT_TABLE(MyFrame, wxFrame)

  101. EVT_MENU(ID_Quit, MyFrame::OnQuit)

  102. EVT_MENU(ID_About, MyFrame::OnAbout)


  103. EVT_MENU(ID_Add, MyFrame::OnAddRectangle)

  104. END_EVENT_TABLE()


  105. BEGIN_EVENT_TABLE(MyCanvas, wxScrolledWindow)

  106. EVT_PAINT(MyCanvas::OnPaint)


  107. EVT_MOTION(MyCanvas::OnMotion)

  108. EVT_LEFT_DOWN(MyCanvas::OnMotion)


  109. EVT_LEFT_UP(MyCanvas::OnMotion)

  110. EVT_ERASE_BACKGROUND(MyCanvas::OnEraseBackGround)
    //This erase flicker create by wxStaticText when erasing background but this is not needed


  111. END_EVENT_TABLE()



  112. //Events **************************


  113. //Overwrite and disable onEraseBackground Event to avoid Flicker

  114. void MyCanvas::OnEraseBackGround(wxEraseEvent& event) {};



  115. void MyCanvas::OnPaint(wxPaintEvent& event)

  116. {

  117. //Prepare Context for Buffered Draw

  118. wxPaintDC dcc(this);


  119. wxBufferedDC dc(&dcc, c_size );

  120. DrawAll(dc);

  121. }



  122. void MyCanvas::OnMotion(wxMouseEvent& event)


  123. {

  124. posx=event.GetPosition().x;

  125. posy=event.GetPosition().y;



  126. this->CalcUnscrolledPosition(posx,posy,&posx,&posy);


  127. if(event.ButtonDown()&&pressed==-1){


  128. pressed=GetIndexSelected();

  129. selected=pressed;


  130. }


  131. if(event.ButtonUp()){

  132. pressed=-1;


  133. }


  134. if (event.Dragging()&&pressed!=-1)

  135. {

  136. rts[pressed][0]=posx-25;


  137. rts[pressed][1]=posy-25;

  138. }



  139. this->Refresh();

  140. }



  141. //**************************Functions

  142. int MyCanvas::GetIndexSelected(){


  143. int index=-1;

  144. for(int i=0;i<rts_Size;i++){


  145. int x=rts[i][0];


  146. int y=rts[i][1];


  147. if( ((posx-x>0)&&(x+70>posx)) && ((posy-y>0)&&(y+70>posy)) ){


  148. return i;

  149. break;


  150. }

  151. }

  152. return index;


  153. }



  154. void MyCanvas::DrawAll(wxBufferedDC& bdc){

  155. bdc.SetBackground(*wxBLACK_BRUSH);


  156. wxFont f = wxFont(8, wxFONTFAMILY_ROMAN, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL);

  157. wxFont f2 = wxFont(10, wxFONTFAMILY_ROMAN, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_BOLD);

  158. bdc.Clear();


  159. bdc.SetBrush(*wxWHITE_BRUSH);

  160. for(int i=(rts_Size-1);i>=0;i--){ //i-- to draw in order


  161. int x=rts[i][0];


  162. int y=rts[i][1];


  163. this->CalcScrolledPosition(x,y,&x,&y);

  164. if(x!=-1 && y!=-1){


  165. if(selected==i){


  166. bdc.SetPen(*wxRED_PEN);


  167. }else{

  168. bdc.SetPen(*wxBLACK_PEN);


  169. }

  170. bdc.DrawRectangle(wxRect(wxPoint(x,y), wxSize(70,70)));


  171. bdc.SetFont(f2);


  172. bdc.DrawText(wxT("TName"),x+5,y);


  173. bdc.SetFont(f);


  174. bdc.DrawText(wxT("CName 1"),x+10,y+12); //use font metrics here


  175. bdc.DrawText(wxT("CName 2"),x+10,y+24); //use font metrics here


  176. bdc.DrawText(wxT("CName 3"),x+10,y+36); //use font metrics here


  177. bdc.DrawText(wxT("CName ..."),x+10,y+36); //use font metrics here


  178. }

  179. else{


  180. break;

  181. }


  182. }

  183. }




  184. //**************************Menu Events

  185. void MyFrame::OnAddRectangle(wxCommandEvent& WXUNUSED(event))


  186. {

  187. if(paintArea->rts_Size < MAXRECTANGLES){

  188. paintArea->rts[paintArea->rts_Size][0]= paintArea->rts_Size*10+10;


  189. paintArea->rts[paintArea->rts_Size][1]= paintArea->rts_Size*10+10;


  190. paintArea->rts_Size++;

  191. }


  192. }

  193. void MyFrame::OnQuit(wxCommandEvent& WXUNUSED(event))

  194. { Close(TRUE);


  195. }

  196. void MyFrame::OnAbout(wxCommandEvent& WXUNUSED(event))

  197. { wxMessageBox(wxT("This is a Hello Direct Contex Application in wxWidgets"),


  198. wxT("About Hello DC"), wxOK | wxICON_INFORMATION, this);

  199. }

No hay comentarios: