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.

Child wxPanel not capturing mouse motion

Postby misha » Fri Jun 15, 2012 4:11 am

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 );
   
}
misha
In need of some credit
In need of some credit
 
Posts: 3
Joined: Fri Jun 15, 2012 3:38 am

Re: Child wxPanel not capturing mouse motion

Postby doublemax » Fri Jun 15, 2012 8:40 am

Code: Select all
   m_image = new wxImagePanel(this);
The imagepanel should get m_panel1 as parent.
Use the source, Luke!
User avatar
doublemax
Part Of The Furniture
Part Of The Furniture
 
Posts: 7156
Joined: Fri Apr 21, 2006 8:03 pm
Location: $FCE2

Re: Child wxPanel not capturing mouse motion

Postby misha » Fri Jun 15, 2012 1:42 pm

Bull's eye! Thank you very much.
misha
In need of some credit
In need of some credit
 
Posts: 3
Joined: Fri Jun 15, 2012 3:38 am

Re: Child wxPanel not capturing mouse motion

Postby Samuel-FES » Wed Jul 25, 2012 8:40 pm

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

Postby doublemax » Wed Jul 25, 2012 9:51 pm

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!
User avatar
doublemax
Part Of The Furniture
Part Of The Furniture
 
Posts: 7156
Joined: Fri Apr 21, 2006 8:03 pm
Location: $FCE2

Re: Child wxPanel not capturing mouse motion

Postby Samuel-FES » Wed Jul 25, 2012 10:28 pm

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

Postby doublemax » Wed Jul 25, 2012 10:35 pm

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!
User avatar
doublemax
Part Of The Furniture
Part Of The Furniture
 
Posts: 7156
Joined: Fri Apr 21, 2006 8:03 pm
Location: $FCE2

Re: Child wxPanel not capturing mouse motion

Postby Samuel-FES » Thu Jul 26, 2012 6:30 pm

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^^
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

Postby doublemax » Thu Jul 26, 2012 6:48 pm

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!
User avatar
doublemax
Part Of The Furniture
Part Of The Furniture
 
Posts: 7156
Joined: Fri Apr 21, 2006 8:03 pm
Location: $FCE2

Re: Child wxPanel not capturing mouse motion

Postby Samuel-FES » Thu Jul 26, 2012 7:02 pm

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)
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

Postby doublemax » Thu Jul 26, 2012 7:10 pm

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!
User avatar
doublemax
Part Of The Furniture
Part Of The Furniture
 
Posts: 7156
Joined: Fri Apr 21, 2006 8:03 pm
Location: $FCE2

Re: Child wxPanel not capturing mouse motion

Postby Samuel-FES » Thu Jul 26, 2012 7:15 pm

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

Postby doublemax » Thu Jul 26, 2012 7:37 pm

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!
User avatar
doublemax
Part Of The Furniture
Part Of The Furniture
 
Posts: 7156
Joined: Fri Apr 21, 2006 8:03 pm
Location: $FCE2

Re: Child wxPanel not capturing mouse motion

Postby Samuel-FES » Thu Jul 26, 2012 7:38 pm

that definitely makes more sense by playing around event method tho

thanks!
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

Postby PB » Thu Jul 26, 2012 7:49 pm

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.
PB
Ultimate wxWidgets Guru
Ultimate wxWidgets Guru
 
Posts: 596
Joined: Sun Jan 03, 2010 5:45 pm

Next

Return to C++ Development

Who is online

Users browsing this forum: No registered users and 3 guests