Child wxPanel not capturing mouse motion

If you are using the main C++ distribution of wxWidgets, Feel free to ask any question related to wxWidgets development here. This means questions regarding to C++ and wxWidgets, not compile problems.
misha
In need of some credit
In need of some credit
Posts: 5
Joined: Fri Jun 15, 2012 3:38 am

Child wxPanel not capturing mouse motion

Post by misha »

I have a simple GUI application that looks like this:

Image

What I want to achieve mouse motion inside the wxImagePanel widget (which I grabbed from here). When the user drags on the wxImagePanel, then the application draws onto the wxImagePanel. The source is here:

wxImagePanel.hpp:

Code: Select all

#ifndef WX_IMAGE_PANEL_HPP
#define WX_IMAGE_PANEL_HPP
#include <wx/wx.h>
#include <wx/sizer.h>
class wxImagePanel : public wxPanel
{
    wxImage m_image;
public:
    wxImagePanel(wxFrame* parent);   
    void paintEvent(wxPaintEvent & evt);
    void paintNow();    
    void render(wxDC& dc);
    void mouseMoved(wxMouseEvent& event);  
    DECLARE_EVENT_TABLE()
};
#endif
wxImagePanel.cpp:

Code: Select all

#include "wxImagePanel.hpp"  
BEGIN_EVENT_TABLE(wxImagePanel, wxPanel)
EVT_PAINT(wxImagePanel::paintEvent)
EVT_MOTION(wxImagePanel::mouseMoved)
END_EVENT_TABLE()
void wxImagePanel::mouseMoved(wxMouseEvent& event) 
{
    if (event.Dragging())
    {
        m_image.SetRGB(event.m_x, event.m_y, 255, 0, 0);
        paintNow();
    }
}
wxImagePanel::wxImagePanel(wxFrame* parent) :
wxPanel(parent)
{
    m_image = wxImage(640, 480, true);
}
void wxImagePanel::paintEvent(wxPaintEvent & evt)
{
    wxPaintDC dc(this);
    render(dc);
}
void wxImagePanel::paintNow()
{
    wxClientDC dc(this);
    render(dc);
}
void wxImagePanel::render(wxDC&  dc)
{
    dc.DrawBitmap(m_image, 0, 0, false );
}
app.h:

Code: Select all

#ifndef APP_HPP
#define APP_HPP
#include <wx/wx.h>
#include "noname.h"
class MyApp : public wxApp
{
    virtual bool OnInit();
};
class MyFrame1Impl : public MyFrame1
{
    public:
        MyFrame1Impl();
        virtual ~MyFrame1Impl();
    protected:
        void onMotion(wxMouseEvent &event);
};
#endif
app.cpp:

Code: Select all

#include "app.hpp"
IMPLEMENT_APP(MyApp)
bool 
MyApp::OnInit()
{
    MyFrame1 *frame = new MyFrame1Impl();
    frame->Show(true);
    SetTopWindow(frame);
    return true;
}
MyFrame1Impl::MyFrame1Impl() : MyFrame1(NULL) { }
MyFrame1Impl::~MyFrame1Impl() { }
void 
MyFrame1Impl::onMotion(wxMouseEvent &event)
{
    wxString label =
        wxString::Format
        (
            wxT("MyFrame1Impl::onMotion(): x: %d y: %d"), 
            event.m_x, 
            event.m_y
        );
    m_staticText1->SetLabel(label);
    event.Skip();
}
And now, the GUI, which I built using wxFormBuilder:

noname.h:

Code: Select all

///////////////////////////////////////////////////////////////////////////
// C++ code generated with wxFormBuilder (version Jun 30 2011)
// http://www.wxformbuilder.org/
//
// PLEASE DO "NOT" EDIT THIS FILE!
///////////////////////////////////////////////////////////////////////////

#ifndef __NONAME_H__
#define __NONAME_H__

#include <wx/artprov.h>
#include <wx/xrc/xmlres.h>
#include "wxImagePanel.hpp"
#include <wx/string.h>
#include <wx/gdicmn.h>
#include <wx/font.h>
#include <wx/colour.h>
#include <wx/settings.h>
#include <wx/stattext.h>
#include <wx/sizer.h>
#include <wx/frame.h>

///////////////////////////////////////////////////////////////////////////


///////////////////////////////////////////////////////////////////////////////
/// Class MyFrame1
///////////////////////////////////////////////////////////////////////////////
class MyFrame1 : public wxFrame 
{
	private:
	
	protected:
		wxImagePanel *m_image;
		wxStaticText* m_staticText1;
		
		// Virtual event handlers, overide them in your derived class
		virtual void onMotion( wxMouseEvent& event ) { event.Skip(); }
		
	
	public:
		
		MyFrame1( wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = wxEmptyString, const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxSize( 500,300 ), long style = wxDEFAULT_FRAME_STYLE|wxTAB_TRAVERSAL );
		
		~MyFrame1();
	
};

#endif //__NONAME_H__

noname.cpp:

Code: Select all

///////////////////////////////////////////////////////////////////////////
// C++ code generated with wxFormBuilder (version Jun 30 2011)
// http://www.wxformbuilder.org/
//
// PLEASE DO "NOT" EDIT THIS FILE!
///////////////////////////////////////////////////////////////////////////

#include "noname.h"

///////////////////////////////////////////////////////////////////////////

MyFrame1::MyFrame1( wxWindow* parent, wxWindowID id, const wxString& title, const wxPoint& pos, const wxSize& size, long style ) : wxFrame( parent, id, title, pos, size, style )
{
	this->SetSizeHints( wxDefaultSize, wxDefaultSize );
	
	wxBoxSizer* bSizer1;
	bSizer1 = new wxBoxSizer( wxVERTICAL );
	
	m_image = new wxImagePanel(this);
	bSizer1->Add( m_image, 1, wxALL|wxEXPAND, 5 );
	
	m_staticText1 = new wxStaticText( this, wxID_ANY, wxT("MyLabel"), wxDefaultPosition, wxDefaultSize, 0 );
	m_staticText1->Wrap( -1 );
	bSizer1->Add( m_staticText1, 0, wxALL, 5 );
	
	this->SetSizer( bSizer1 );
	this->Layout();
	
	this->Centre( wxBOTH );
	
	// Connect Events
	this->Connect( wxEVT_MOTION, wxMouseEventHandler( MyFrame1::onMotion ) );
}

MyFrame1::~MyFrame1()
{
	// Disconnect Events
	this->Disconnect( wxEVT_MOTION, wxMouseEventHandler( MyFrame1::onMotion ) );
	
}
So far, everything works, but it looks really ugly because of the dark gray background on Windows. The recommended way to fix this is to add a parent wxPanel to the wxFrame.

The result looks like this:

Image

This looks a bit better, but now the mouse events are no longer being captured by the wxImagePanel. They go straight to the top wxPanel, which is not what I want. How do I make sure the events get to the child wxImagePanel?

The updated source for the GUI is as follows:

noname.h:

Code: Select all

///////////////////////////////////////////////////////////////////////////
// C++ code generated with wxFormBuilder (version Jun 30 2011)
// http://www.wxformbuilder.org/
//
// PLEASE DO "NOT" EDIT THIS FILE!
///////////////////////////////////////////////////////////////////////////

#ifndef __NONAME_H__
#define __NONAME_H__

#include <wx/artprov.h>
#include <wx/xrc/xmlres.h>
#include "wxImagePanel.hpp"
#include <wx/string.h>
#include <wx/gdicmn.h>
#include <wx/font.h>
#include <wx/colour.h>
#include <wx/settings.h>
#include <wx/stattext.h>
#include <wx/sizer.h>
#include <wx/panel.h>
#include <wx/frame.h>

///////////////////////////////////////////////////////////////////////////


///////////////////////////////////////////////////////////////////////////////
/// Class MyFrame1
///////////////////////////////////////////////////////////////////////////////
class MyFrame1 : public wxFrame 
{
	private:
	
	protected:
		wxPanel* m_panel1;
		wxImagePanel *m_image;
		wxStaticText* m_staticText1;
		
		// Virtual event handlers, overide them in your derived class
		virtual void onMotion( wxMouseEvent& event ) { event.Skip(); }
		
	
	public:
		
		MyFrame1( wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = wxEmptyString, const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxSize( 500,300 ), long style = wxDEFAULT_FRAME_STYLE|wxTAB_TRAVERSAL );
		
		~MyFrame1();
	
};

#endif //__NONAME_H__
noname.cpp:

Code: Select all

///////////////////////////////////////////////////////////////////////////
// C++ code generated with wxFormBuilder (version Jun 30 2011)
// http://www.wxformbuilder.org/
//
// PLEASE DO "NOT" EDIT THIS FILE!
///////////////////////////////////////////////////////////////////////////

#include "noname.h"

///////////////////////////////////////////////////////////////////////////

MyFrame1::MyFrame1( wxWindow* parent, wxWindowID id, const wxString& title, const wxPoint& pos, const wxSize& size, long style ) : wxFrame( parent, id, title, pos, size, style )
{
	this->SetSizeHints( wxDefaultSize, wxDefaultSize );
	
	wxBoxSizer* bSizer1;
	bSizer1 = new wxBoxSizer( wxVERTICAL );
	
	m_panel1 = new wxPanel( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL );
	wxBoxSizer* bSizer2;
	bSizer2 = new wxBoxSizer( wxVERTICAL );
	
	m_image = new wxImagePanel(this);
	bSizer2->Add( m_image, 1, wxALL|wxEXPAND, 5 );
	
	m_staticText1 = new wxStaticText( m_panel1, wxID_ANY, wxT("MyLabel"), wxDefaultPosition, wxDefaultSize, 0 );
	m_staticText1->Wrap( -1 );
	bSizer2->Add( m_staticText1, 0, wxALL, 5 );
	
	m_panel1->SetSizer( bSizer2 );
	m_panel1->Layout();
	bSizer2->Fit( m_panel1 );
	bSizer1->Add( m_panel1, 1, wxEXPAND | wxALL, 5 );
	
	this->SetSizer( bSizer1 );
	this->Layout();
	
	this->Centre( wxBOTH );
	
	// Connect Events
	m_panel1->Connect( wxEVT_MOTION, wxMouseEventHandler( MyFrame1::onMotion ), NULL, this );
}

MyFrame1::~MyFrame1()
{
	// Disconnect Events
	m_panel1->Disconnect( wxEVT_MOTION, wxMouseEventHandler( MyFrame1::onMotion ), NULL, this );
	
}
User avatar
doublemax
Moderator
Moderator
Posts: 19116
Joined: Fri Apr 21, 2006 8:03 pm
Location: $FCE2

Re: Child wxPanel not capturing mouse motion

Post by doublemax »

Code: Select all

   m_image = new wxImagePanel(this);
The imagepanel should get m_panel1 as parent.
Use the source, Luke!
misha
In need of some credit
In need of some credit
Posts: 5
Joined: Fri Jun 15, 2012 3:38 am

Re: Child wxPanel not capturing mouse motion

Post by misha »

Bull's eye! Thank you very much.
Samuel-FES
Knows some wx things
Knows some wx things
Posts: 35
Joined: Thu May 03, 2012 9:01 pm

Re: Child wxPanel not capturing mouse motion

Post by Samuel-FES »

Hey guys I am here to beat dead horses...

I am trying to capture the mouse motion globally in the main wxFrame.
So I managed to do that with a thread specifically in charge of this + wxMouseEvent

The problem is that the coordinates are apparently not changing by mouse moving when the cursor is in a wxPanel in my wxFrame.

I suppose that assigning this event thread to the wxFrame should allow cursor capture motion everywhere in the wxFrame...
User avatar
doublemax
Moderator
Moderator
Posts: 19116
Joined: Fri Apr 21, 2006 8:03 pm
Location: $FCE2

Re: Child wxPanel not capturing mouse motion

Post by doublemax »

I hope when you say "thread", you don't mean a secondary thread.

Are you actually cally wxWindow::CaptureMouse() on the frame?

If possible create a minimal compilable sample that shows the problem.
Use the source, Luke!
Samuel-FES
Knows some wx things
Knows some wx things
Posts: 35
Joined: Thu May 03, 2012 9:01 pm

Re: Child wxPanel not capturing mouse motion

Post by Samuel-FES »

lol technically i didnt set the thread up so nothing to be done with thread anymore.

I actually declared a mouse event at the event table:

Code: Select all

BEGIN_EVENT_TABLE(ManagerFrame,wxFrame)
    EVT_MOTION (ManagerFrame::OnCoordMotion)
END_EVENT_TABLE()
Then I created a method for my ManagerFrame:

Code: Select all

void ManagerFrame::OnCoordMotion(wxMouseEvent& event)
{
    long x = event.GetX(), y = event.GetY();
    std::stringstream s1,s2;
    s1<<x;
    s2<<y;

    std::string ss1, ss2;
    s1>>ss1;
    s2>>ss2;

    ss1.append("@");
    ss1.append(ss2);
    wxString temp = wxString::FromUTF8(ss1.c_str());

    StaticText1->SetLabel(temp);
}

which is obviously updating the current cursor's coordinates to StaticText.

I realized that motions in wxPanel can be captured.Then I added a wxStaticBitmap in wxPanel. Then the coordinates stop updating.
User avatar
doublemax
Moderator
Moderator
Posts: 19116
Joined: Fri Apr 21, 2006 8:03 pm
Location: $FCE2

Re: Child wxPanel not capturing mouse motion

Post by doublemax »

Yes, mouse events normally go to the control under the mouse pointer. If you have panel with a staticbitmap on it, the events will go the staticbitmap when the mouse pointer is over it.

The only way to change that is to call wxWindow::CaptureMouse(). Or you have to catch the mouse events of all child controls too.
Use the source, Luke!
Samuel-FES
Knows some wx things
Knows some wx things
Posts: 35
Joined: Thu May 03, 2012 9:01 pm

Re: Child wxPanel not capturing mouse motion

Post by Samuel-FES »

i tried wxWindow::CaptureMouse() and definitely it works well and coordinates are captured correctly.
however,when the program is capturing mouse,the whole program "locks".be like,the cursor moves around and buttons in the interface are all not able to press,and the coordinates are updating

i dont have much experience with wxwidgets and im wondering if it will be possible to merge this operation into a second level thread by wxwidgets event method.

by this means,in the second level thread,apparently wxWindow::CaptureMouse() is working but i might not be able to obtain the coordinates.

thank you^^
User avatar
doublemax
Moderator
Moderator
Posts: 19116
Joined: Fri Apr 21, 2006 8:03 pm
Location: $FCE2

Re: Child wxPanel not capturing mouse motion

Post by doublemax »

i dont have much experience with wxwidgets and im wondering if it will be possible to merge this operation into a second level thread by wxwidgets event method.
No. wxWidgets doesn't allow GUI access from secondary threads.

Do what purpose do you need this functionality? Maybe you can use wxApp::FilterEvent() where all events go through.
http://docs.wxwidgets.org/stable/wx_wxa ... ilterevent
Use the source, Luke!
Samuel-FES
Knows some wx things
Knows some wx things
Posts: 35
Joined: Thu May 03, 2012 9:01 pm

Re: Child wxPanel not capturing mouse motion

Post by Samuel-FES »

hmm i m trying to do a coordinates based drag and drop by capturing the exact coordinates for the next object to drop.

basically, i have a map(loaded to wxStaticBitmap) and an object / person to be parachuted

thats why i really want to work the coordinates out in wxStaticBitmap in a FlexGridSizer(this sizer is the child of wxFrame,which is the very main one)
User avatar
doublemax
Moderator
Moderator
Posts: 19116
Joined: Fri Apr 21, 2006 8:03 pm
Location: $FCE2

Re: Child wxPanel not capturing mouse motion

Post by doublemax »

That's a typical scenario for the use of CaptureMouse(). When the drag starts, you capture the mouse and when it ends, you release it again.
Use the source, Luke!
Samuel-FES
Knows some wx things
Knows some wx things
Posts: 35
Joined: Thu May 03, 2012 9:01 pm

Re: Child wxPanel not capturing mouse motion

Post by Samuel-FES »

so when i start dragging,wxFrame starts capturing mouse
when i decide where to drop(a bomb or candy lol) , wxFrame releases mouse,correct?
and when i release mouse,obviously the object has been set to a certain position.will it be able to grab the coordinates in terms of wxPoint or simply long int...?
cuz i do have some plans for those coordinates for more features in the future lol
User avatar
doublemax
Moderator
Moderator
Posts: 19116
Joined: Fri Apr 21, 2006 8:03 pm
Location: $FCE2

Re: Child wxPanel not capturing mouse motion

Post by doublemax »

The frame doesn't capture/release the mouse by itself. You have to actively do that.

The usual way is this:
In a mouse down event you call CaptureMouse()

Then you keep tracking mouse motion events and do whatever you need to do in that event.

If you detect a mouse-up event, you call ReleaseMouse().

Check the "dragimag" sample that comes with wxWidgets.
Use the source, Luke!
Samuel-FES
Knows some wx things
Knows some wx things
Posts: 35
Joined: Thu May 03, 2012 9:01 pm

Re: Child wxPanel not capturing mouse motion

Post by Samuel-FES »

that definitely makes more sense by playing around event method tho

thanks!
PB
Part Of The Furniture
Part Of The Furniture
Posts: 4193
Joined: Sun Jan 03, 2010 5:45 pm

Re: Child wxPanel not capturing mouse motion

Post by PB »

If you plan your app to be used on MSW, I would also recommend looking into HasCapture() and/or wxMouseCaptureLostEvent and wxMouseCaptureChangedEvent. IIRC, you can easily lose the capture (when the window loses focus) which can sometimes create an unexpected situations.

I don't know why it is not used in dragimag sample, maybe I was doing something wrong that I had to use it though.
Post Reply