I have written a program in c++ using wxwidgets.I am placing a rectangle on the image and want to select the part of image covered by rectangle for which the rectangle should be draggable. But the problem is when I click the mouse the image vanishes and the only rectangle (which can be dragged) remains and it happens vice-versa.
`
class BasicDrawPane : public wxPanel
{
public:
BasicDrawPane();
BasicDrawPane(wxFrame* parent);
void paintEvent(wxPaintEvent & evt);
void render(wxDC& dc);
void mouseMoved(wxMouseEvent& event);
void mouseDown(wxMouseEvent& event);
void mouseWheelMoved(wxMouseEvent& event);
void mouseReleased(wxMouseEvent& event);
void rightClick(wxMouseEvent& event);
void mouseLeftWindow(wxMouseEvent& event);
DECLARE_EVENT_TABLE()
};
class MyFrame: public wxFrame{
public:
MyFrame(const wxString& title, const wxPoint& pos, const wxSize& size);
wxString path;
BasicDrawPane panel;
private:
void OnHello(wxCommandEvent& event);
void OnExit(wxCommandEvent& event);
void OnAbout(wxCommandEvent& event);
void OnOpen(wxCommandEvent& event);
void OnPaint(wxCommandEvent& event);
void OnRect(wxCommandEvent& event);
void OnSave(wxCommandEvent& event);
DECLARE_EVENT_TABLE();
wxBitmap bmp;
wxMemoryDC memDC;
};
enum
{
ID_Hello = 1, ID_PAINT = 2, ID_RECT = 3, ID_SAVE = 4
};
BEGIN_EVENT_TABLE( MyFrame, wxFrame )
EVT_MENU(ID_Hello,MyFrame::OnHello)
EVT_MENU(wxID_EXIT,MyFrame::OnExit)
EVT_MENU(wxID_ABOUT,MyFrame::OnAbout)
EVT_MENU(wxID_OPEN,MyFrame::OnOpen)
EVT_MENU(ID_PAINT,MyFrame::OnPaint)
EVT_MENU(ID_RECT,MyFrame::OnRect)
EVT_MENU(ID_SAVE,MyFrame::OnSave)
END_EVENT_TABLE()
void MyFrame::OnPaint(wxCommandEvent& event)
{
//wxPaintDC dc( this );
//dc.DrawBitmap( m_bitmap, 0, 0, true /* use mask */ );
//wxStaticBitmap *b1 = new wxStaticBitmap(this, -1, wxBitmap(wxImage(path)));
bmp.LoadFile((path),wxBITMAP_TYPE_ANY);
// bmp.LoadFile((path),wxBITMAP_TYPE_PNG);
memDC.SelectObject( bmp );
//memDC.SetBackground(*wxWHITE_BRUSH);
//memDC.Clear();
/* memDC.SetPen(*wxGREEN_PEN);
memDC.SetBrush(*wxTRANSPARENT_BRUSH);
memDC.DrawRectangle( m_x, m_y, WIDTH, HEIGHT );*/
//Check();
memDC.SelectObject(wxNullBitmap);
// wxSize sz(512,384);
// wxSize sz(900,600);
wxStaticBitmap *b1 = new wxStaticBitmap(/* dynamic_cast<wxFrame*>*/this, -1, bmp, wxDefaultPosition);
Refresh();
}
class MyApp: public wxApp
{
public:
virtual bool OnInit();
//MyFrame *frame;
BasicDrawPane * drawPane;
};
IMPLEMENT_APP(MyApp)
bool MyApp::OnInit()
{
// wxBoxSizer* sizer = new wxBoxSizer(wxHORIZONTAL);
//frame = new MyFrame((wxFrame *)NULL, -1, wxT("Hello wxDC"), wxPoint(50,50), wxSize(800,600));
MyFrame *frame = new MyFrame( _T("Hello World"), wxPoint(50, 50), wxSize(600, 600) );
// drawPane = new BasicDrawPane( (wxFrame*) frame );
// sizer->Add(drawPane, 1, wxEXPAND);
//frame->SetSizer(sizer);
// /* dynamic_cast<wxFrame*>(this)*/ frame-> SetAutoLayout(true);
/* dynamic_cast<wxFrame*>(this)*/frame -> Show();
return true;
}
BEGIN_EVENT_TABLE(BasicDrawPane, wxPanel)
EVT_MOTION(BasicDrawPane::mouseMoved)
EVT_LEFT_DOWN(BasicDrawPane::mouseDown)
EVT_LEFT_UP(BasicDrawPane::mouseReleased)
EVT_RIGHT_DOWN(BasicDrawPane::rightClick)
EVT_LEAVE_WINDOW(BasicDrawPane::mouseLeftWindow)
EVT_MOUSEWHEEL(BasicDrawPane::mouseWheelMoved)
EVT_PAINT(BasicDrawPane::paintEvent)
// catch paint events
END_EVENT_TABLE()
void BasicDrawPane::mouseDown(wxMouseEvent& event)
{
/* if (event.GetPosition().x >= m_x && event.GetPosition().x <= m_x + WIDTH &&
event.GetPosition().y >= m_y && event.GetPosition().y <= m_y + HEIGHT)
{
m_dragging = true;
m_previous_mouse_x = event.GetPosition().x;
m_previous_mouse_y = event.GetPosition().y;
}*/
}
void BasicDrawPane::mouseWheelMoved(wxMouseEvent& event) {}
void BasicDrawPane::mouseReleased(wxMouseEvent& event)
{
m_dragging = true;
}
void BasicDrawPane::mouseMoved(wxMouseEvent& event)
{
if (m_dragging && event.Dragging())
{
int delta_x = event.GetPosition().x - m_previous_mouse_x;
int delta_y = event.GetPosition().y - m_previous_mouse_y;
m_x += delta_x;
m_y += delta_y;
m_previous_mouse_x = event.GetPosition().x;
m_previous_mouse_y = event.GetPosition().y;
// trigger paint event
Refresh();
}
}
void BasicDrawPane::mouseLeftWindow(wxMouseEvent& event)
{
m_dragging = true;
}
void BasicDrawPane::rightClick(wxMouseEvent& event) {}
BasicDrawPane::BasicDrawPane(wxFrame* parent) :
wxPanel(parent)
{
// m_dragging = true;
// m_x = 100;
// m_y = 100;
}
/*
* Called by the system of by wxWidgets when the panel needs
* to be redrawn. You can also trigger this call by
* calling Refresh()/Update().
*/
void BasicDrawPane::paintEvent(wxPaintEvent & evt)
{
//wxCommandEvent w1(wxEVT_NULL, ID_PAINT);
//OnPaint(w1);
wxPaintDC dc(this);
render(dc);
}
void BasicDrawPane::render(wxDC& dc)
{
dc.SetPen(*wxGREEN_PEN);
dc.SetBrush(*wxTRANSPARENT_BRUSH);
dc.DrawRectangle( m_x, m_y, WIDTH, HEIGHT );
}
`
There are several things to explain in order to answer this question, so I will take them one at a time. I think your basic idea is okay, so I won't go into a lot of detail on how the actual selection should take place etc.
Firstly, I would recommend to use either Connect() or Bind() instead of an event table. This allows you to connect child window events back to the parent window and handle them all in one place.
For example, if your main frame class is called MainFrame
and you have a wxPanel
member called m_DrawPanel
, in the MainFrame ctor you could have:
MainFrame::MainFrame(wxWindow* parent)
{
// Connect mouse event handlers.
m_DrawPanel->Connect(wxEVT_LEFT_DOWN,
wxMouseEventHandler(MainFrame::OnPanelLDown), NULL, this);
m_DrawPanel->Connect(wxEVT_LEFT_UP,
wxMouseEventHandler(MainFrame::OnPanelLUp), NULL, this);
m_DrawPanel->Connect(wxEVT_MOTION,
wxMouseEventHandler(MainFrame::OnPanelMotion), NULL, this);
// Connect paint and erase handlers.
m_DrawPanel->Connect(wxEVT_PAINT,
wxPaintEventHandler(MainFrame::OnPanelPaint), NULL, this);
m_DrawPanel->Connect(wxEVT_ERASE_BACKGROUND,
wxEraseEventHandler(MainFrame::OnPanelErase), NULL, this);
// Load the bitmap and set the mode to 'not currently selecting'.
m_Picture.LoadFile ("wxwidgets.png", wxBITMAP_TYPE_PNG);
m_SelectionMode = false;
}
Note: I included a wxEVT_ERASE_BACKGROUND
event override because otherwise panels tend to be cleared which leads to flicker (this is one simple approach).
The 3 mouse event handlers can implement your selection logic (I think this is basically what you are intending already):
void MainFrame::OnPanelLDown(wxMouseEvent& event)
{
m_SelectionMode = true;
m_SelectionRect = wxRect(event.GetPosition(), wxSize(0, 0));
}
void MainFrame::OnPanelLUp(wxMouseEvent& event)
{
m_SelectionMode = false;
// ... handle what to do with the selection here
// (selected area is defined by m_SelectionRect).
// ...
// Zero the selection rectangle for neatness (not really required).
m_SelectionRect = wxRect ();
}
void MainFrame::OnPanelMotion(wxMouseEvent& event)
{
m_SelectionRect = wxRect(m_SelectionRect.GetTopLeft(), event.GetPosition());
// Call Refresh() to trigger a paint event.
m_mainPanel->Refresh();
}
As mentioned earlier, override the panel's wxEVT_ERASE_BACKGROUND
event to do nothing:
void MainFrame::OnPanelErase(wxEraseEvent& event)
{
}
Finally, I think this is the bit that you really are asking in your question (I included the others to help you build a working program):
void MainFrame::OnPanelPaint(wxPaintEvent& event)
{
// Obtain a wxPaintDC.
wxPaintDC pdc (m_mainPanel);
// Draw our image.
pdc.DrawBitmap(m_Picture, wxPoint(0, 0));
// If the user is currently selecting (left mouse button is down)
// then draw the selection rectangle.
if (m_SelectionMode)
{
pdc.SetPen(*wxRED_PEN);
pdc.SetBrush(*wxTRANSPARENT_BRUSH);
pdc.DrawRectangle(m_SelectionRect);
}
}
This is a paint event handler, so first we need to create a wxPaintDC
context. Next, we paint the bitmap, this ensures it is refreshed every time and not damaged by mouse movements, resizing or other windows being dragged etc. Finally, if the user is currently moving the mouse with the left button pressed, then draw the selection rectangle.
There are many other ways of achieving the same thing. Some of them are possibly better, or more efficient, however this is a simple working way until you become more familiar with wxWidgets.