lunes, 12 de mayo de 2008

First protoype of user interface for the Graphical Query Builder of pgAdmin


Yesterday, I finish what I believe it's the first prototype for pgAdmin GQB (Graphical Query Builder) interface, I have some ideas from my other project in the same area but I need to test the prototypes looking for best usability performance and effectiveness, because I think that doing things good in a quickly way is better than do it in a good looking way but slower. Just as google searching tool: Not big graphics, not best user interface of the web, but usability and effectiveness it's the best one.



By the way, I was thinking about finishing vsqlbuilder after the GQB for pgadmin, I really believe that this two tools can be used for different segments of users:
  • pgAdmin (C++/WxWidgets): Database Administrators [it's my own opinion ;) ]
  • Visual SQL Builder (Java): Database End Users, because I want to include on it: the GQB, reporting tools, little CASE of simple transactional applications & more end user related thing. I just need some time to do it and by september I maybe start to coding something on it.

domingo, 11 de mayo de 2008

Design Patterns


I was thinking about the class hierarchy for my project and think about which design patterns could be useful for this goal, and a quick search on google bring to me, this very good resume about design patterns, useful for choosing one when programming:

http://www.mcdonaldland.info/files/designpatterns/designpatternscard.pdf

Update:
I found this wonderful book about Design Patterns: Head First Design Patterns

Wow it's the first time on my life that have been fun to read a technical book, this is one it's the best introduction to object-oriented programming that I have read. (It's for java but a lot of concepts can be used in other programming languages). And not only funny to read, the most important thing: you learn a lot with it.

You can read sample chapter at o'reilly.

Summer of Code 2008 Students and Mentors Geoposition (Google Map)


Today, Andrei Bautu announced on the gsoc students list, that create a google map containing all (authorized) gsoc students & mentors geoposition, (I assume joining together individual information from every project page at code.google.com/soc/) and put on this url:

http://fmc.anmb.ro/news/2008/google-code-summer-code-2008-students-and-mentors

It's very interesting.

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

Wiki page about pgAdmin Internals

A few days ago, create a wiki page about pgAdmin internals, it's only a stub right now, but I want this page become a quick introduction guide for all new developers at pgAdmin.

Wiki it's located at:

http://wiki.postgresql.org/wiki/PgAdmin_Internals

lunes, 5 de mayo de 2008

Studying Cross-Platform GUI Programming with wxWidgets

This last two weeks I have been learning all about wxWidgets library, but until a few days ago I discover this wonderful book:

Cross-Platform GUI Programming with wxWidgets

This book is available from several sources like amazon, informit and others but in my country (Venezuela) there is a limit in a year (400$ a year) for internet buying (using foreing currency as $), and I already reached my limit, because this I only have access to sample chapter, but I found a wonderful thing:

If I do click in downloads there is available the full version of the book in pdf for free, this is great, and I'm going to use it in this way for this year, but the next year (the limit is for each year) I'm going to buy it to support the authors of the book.

Don't worry book is free because it's part of Bruce Perens' Open Source Series:

http://www.informit.com/imprint/series_detail.aspx?ser=335494

In this series the electronic versions will be made available at no cost several months after each book's publication.

;)

Here it's the full download url:

http://www.informit.com/content/images/0131473816/downloads/0131473816_book.pdf

Update #1: Other useful book found: "Subversion Version Control: Using the Subversion Version Control System in Development Projects"
url: http://www.informit.com/store/product.aspx?isbn=0131855182 [Downloads Area]

domingo, 4 de mayo de 2008

WxOGL vs WxDC vs WxArt2D

This Week I was working in the graphical part of the Query Builder and search for all possible options to implement it using wxWidgets, as part of my research I found only three probably options (there are more options but only take in count this by now) where I run and write some test application for evaluation purposes:


1. WxArt2D:
is a graphical library for 2D, the drawing document can be made hierarchical by adding child primitives to a parent primitive, everything that is drawn from within a document, is done by calling drawing methods via a a2dCanvasView through it's device context a2dDrawer2D. A document can have several views open at the same time. And each view can display whatever part of the document. Source: http://wiki.wxwidgets.org/WxArt2d

Pros:
  • It's a multi-document framework.
  • Can draw complex objects in a easy way.
  • Can exists several views of objects in hierarchy.
  • Can save image to svg format.
  • Allow to handled events in graphical objects.
  • It's Highly optimized.
  • Good community support.
Cons:
  • The only way to use it, is downloading directly from CVS.
  • Configuration, Compilation and use can be difficult for begginners because cmake haven't a useful way of explain errors and their solutions (even the simplest).
  • Adds a new level of complexity in a project when the learning curve of understanding of source code grows because use it.
  • If a error related to the library is raised when you use an application (that happens to me with the examples) only have two options: fix by myself (I don't want to do it ) or ask for help (can take long time).
  • I try a demo of electronic parts diagram and I catch a lot of problems and exceptions only testing, without really using it, this don't give me a good advice about stability.
2. WxOGL: it's a good graphical library written when dinosaurs ruled the world :), but in my own words is a very good objects graphical library written in C++ that allow to create some simple and complex objects given to it the possibility of add events & properties.

Pros:
  • Very easy to use.
  • Configuration and use is very simple.
  • Used by pgAdmin right now, not new dependencies needed.
  • Good Stability.
Cons:
  • Poor documented.
  • Doesn't have support of a community.
  • Development status dead.
  • Don't give all control I want from my graphical objects.
  • Library is not good for and architectural pattern like Model View Controller (MVC).
  • Adds an extra overhead for simple graphics needed, forcing to create complex objects that some times are not needed.
  • Use wxDC as their drawing method, not really a cons but an interesting afirmation.
3. WxDC: device context graphic library, allows you to draw stuff manually.

Pros:
  • Have a lot of flavors: wxPaintDC for paint events, wxClientDC can be used outside paint events, wxBufferedDC for flickerless Drawing, Can paint into postscript files with wxPostScriptDC .
  • Give all control to implement the design pattern that I want to use (MVC).
  • It's very stable because their operations are very simple.
  • Allow full control over the way stuff will be painted.
  • Very simple to learn and use.
  • It's integrated with the wxWidgets library not need aditional steps or configurations.
  • Because their simplicity and integration with wx library, it's most probably don't have any issues when compiling source code in different plataforms
  • Very well documented WxWidgets Book Chapter Sample
Cons:
  • It's not as powerful as WxArt2D or WxOGL.
  • All drawing stuff are now responsability of programmer.
  • A bad implementation of drawing maybe can finish with a buggy application.
  • A functional application can take a little more time (not too much) to be completed that using WxOGL or WxArt2D.

My Choice until now it's: WxDC because their simplicity and the full control that give to programmer of doing almost all things needed for a graphical query builder only using graphics primitives (in a pattern like MVC).

I create a very little (and not optimized or good programmed, just functional) hello graphic world proof of concept for a diagram of tables, you can drag and drop tables (just simple squares with text) around application only:

#include "wx/wxprec.h"

#ifdef __BORLANDC__
#pragma hdrstop
#endif

#ifndef WX_PRECOMP
#endif

#include "wx/wx.h"

class MyApp: public wxApp
{
virtual bool OnInit();
};


IMPLEMENT_APP(MyApp)



class MyFrame: public wxFrame
{
public:

MyFrame(const wxString& title,
const wxPoint& pos, const wxSize& size);

void OnQuit(wxCommandEvent& event);
void OnAbout(wxCommandEvent& event);
void MyFrame::OnPaint(wxPaintEvent& event);
void MyFrame::OnMotion(wxMouseEvent& event);
wxRect rectToDraw;
wxRect rectToDraw2;
int xx, xx2;
int yy, yy2;
int pressed;
DECLARE_EVENT_TABLE()
};


bool MyApp::OnInit()
{
MyFrame *frame = new MyFrame( wxT("Hello World"),
wxPoint(50,50), wxSize(800,600) );
frame->Show(TRUE);
SetTopWindow(frame);
frame->xx=10;
frame->yy=10;
frame->xx2=35;
frame->yy2=35;
frame->pressed=0;
return TRUE;
}


enum
{
ID_Quit = 1,
ID_About = 0,

};

BEGIN_EVENT_TABLE(MyFrame, wxFrame)
EVT_MENU(ID_Quit, MyFrame::OnQuit)
EVT_MENU(ID_About, MyFrame::OnAbout)
EVT_PAINT(MyFrame::OnPaint)
EVT_MOTION(MyFrame::OnMotion)
EVT_LEFT_DOWN(MyFrame::OnMotion)
EVT_LEFT_UP(MyFrame::OnMotion)
END_EVENT_TABLE()



void MyFrame::OnPaint(wxPaintEvent& event)
{
wxPaintDC dc(this);
dc.SetPen(*wxBLACK_PEN);

wxRect tmp(wxPoint(xx, yy), wxSize(70,70));
rectToDraw = tmp;

wxRect tmp2(wxPoint(xx2, yy2), wxSize(70,70));
rectToDraw2 = tmp2;
//Do not draw if not exposed
if (IsExposed(rectToDraw)){
//draw object 1
dc.SetBrush(*wxBLUE_BRUSH);
dc.DrawRectangle(rectToDraw);
dc.DrawText(wxT("Table 1"),xx,yy);
dc.DrawText(wxT("Col1"),xx,yy+12); //use font metrics here
//draw object 2
dc.SetBrush(*wxWHITE_BRUSH);
dc.DrawRectangle(rectToDraw2);
dc.DrawText(wxT("Table 2"),xx2,yy2);
dc.DrawText(wxT("Col1"),xx2,yy2+12);
}
}


MyFrame::MyFrame(const wxString& title,
const wxPoint& pos, const wxSize& size)
: wxFrame((wxFrame *)NULL, -1, title, pos, size)
{
wxMenu *menuFile = new wxMenu;
menuFile->Append( ID_About, wxT("&About..." ));
menuFile->AppendSeparator();
menuFile->Append( ID_Quit, wxT("E&xit" ));

wxMenuBar *menuBar = new wxMenuBar;
menuBar->Append( menuFile, wxT("&File") );

SetMenuBar( menuBar );

CreateStatusBar();
SetStatusText( wxT("Proof of Concept drawing moving objects, just click one and drag & drop [flicker can be eliminated with buffering]") );
}


void MyFrame::OnQuit(wxCommandEvent& WXUNUSED(event))
{
Close(TRUE);
}

void MyFrame::OnAbout(wxCommandEvent& WXUNUSED(event))
{

wxMessageBox(wxT("This is a Hello Direct Contex Application in wxWidgets"),
wxT("About Hello DC"), wxOK | wxICON_INFORMATION, this);
}


//Change position of objects when mouse is dragged & refresh painting
void MyFrame::OnMotion(wxMouseEvent& event)
{
if(event.ButtonDown()&&pressed==0){
pressed=1;
}
if(event.ButtonUp()){
pressed=0;
}

if (event.Dragging())
{
wxClientDC dc(this);
wxPen pen(*wxRED, 1); // red pen of width 1
dc.SetPen(pen);
dc.DrawPoint(event.GetPosition());
dc.SetPen(wxNullPen);
int posx=event.GetPosition().x;
int posy=event.GetPosition().y;
if(pressed==1 &&( posx-xx>0 && xx+50>posx ) && (posy-yy>0 && yy+50>posy)){
pressed=2;
}else if(pressed==1 &&( posx-xx2>0 && xx2+50>posx ) && (posy-yy2>0 && yy2+50>posy)){
pressed=3;
}

if(pressed==2){
xx=posx-25;
yy=posy-25;
}else if (pressed==3){
xx2=posx-25;
yy2=posy-25;
}

this->Refresh();
}
}