Strange behaviour in DC Topic is solved

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.
dkaip
Super wx Problem Solver
Super wx Problem Solver
Posts: 334
Joined: Wed Jan 20, 2010 1:15 pm

Strange behaviour in DC

Post by dkaip »

After image zoom i can see the line. But after exit from UpdateBitmap() function line disappear.
But i don’t understand why.

Code: Select all

class ImageControl : public wxFrame
---------
private:
    wxDC* frameDC;
    ImageViewer *m_viewer;
-----------

void ImageControl::OnZoomOut(wxCommandEvent& event)
{
    event.Skip(true);
    m_viewer->SetZoom(m_viewer->GetZoom() / ZOOM_FACTOR_STEP);
    UpdateStatus();
}

void ImageControl::UpdateStatus()
{
    frameDC=m_viewer->GetDC();
    frameDC->DrawLine(0,0,200,200);
}



class ImageViewer : public wxScrolledWindow ...
... 
private:
    wxDC* dc;
...

void ImageViewer::Set(const wxImage& image)
{
    m_orig_image = image;
    UpdateBitmap();
}


void ImageViewer::UpdateBitmap()
{
    int new_w = int(m_orig_image.GetWidth() * zoom);
    int new_h = int(m_orig_image.GetHeight() * zoom);
        wxImage scaled =m_orig_image.Scale(new_w,new_h,wxIMAGE_QUALITY_NORMAL);
        m_content->SetBitmap(wxBitmap(scaled));
    GetSizer()->FitInside(this);
    dc=new wxPaintDC(this);
    dc->DrawLine(0,0,200,200);
}
PB
Part Of The Furniture
Part Of The Furniture
Posts: 4204
Joined: Sun Jan 03, 2010 5:45 pm

Re: Strange behaviour in DC

Post by PB »

1. Do not create wxPaintDC outside the paint event handler and do not store it.
2. Always draw all that is needed from/in the paint event handler. otherwise it gets overdrawn by the next window repaint.

I recommend reading some basic drawing tutorials, it will make it clear how to draw with wxWidgets.
dkaip
Super wx Problem Solver
Super wx Problem Solver
Posts: 334
Joined: Wed Jan 20, 2010 1:15 pm

Re: Strange behaviour in DC

Post by dkaip »

Have some url fo some basic drawing tutorials?
Thank's
Jim
PB
Part Of The Furniture
Part Of The Furniture
Posts: 4204
Joined: Sun Jan 03, 2010 5:45 pm

Re: Strange behaviour in DC

Post by PB »

I do not know how good they are, but you could look at those at wxWiki: https://wiki.wxwidgets.org/Guides_%26_T ... ous_Guides

But the basics are very simple, as I said above:
1. You must always be able to draw everything that is needed from the paint event handler.
2. If you need to redraw something because the state/data changed, do not draw it e.g. from the user input handler. In the handler just update the state and force redraw, via Refresh() + Update().
3. Always create wxPaintDC (or its buffered equivalent) in the paint event handler and make sure it gets destroyed there, do not store it for future use (that goes for all wxDCs).


Simplest example of custom drawing, you may need to look up what some things (sucj as wxFULL_REPAINT_ON_RESIZE or SetBackgroundStyle()) are for in the official documentation

Code: Select all

#include <wx/wx.h>
#include <wx/dcbuffer.h>

class MyCanvas : public wxPanel
{
public:
    MyCanvas(wxWindow* parent)
        : wxPanel(parent, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxFULL_REPAINT_ON_RESIZE)
    {        
        m_clicks = 0;

        SetBackgroundStyle(wxBG_STYLE_PAINT);
        Bind(wxEVT_PAINT, &MyCanvas::OnPaint, this);                
    }

    void IncreaseClicks()
    {
        m_clicks++;
        Refresh(); 
        Update();
    }   
private:    
    int m_clicks;

    void OnPaint(wxPaintEvent&) 
    { 
        wxSize size(GetClientSize());
        wxPoint center(size.GetWidth() / 2, size.GetHeight() / 2);        
        
        wxAutoBufferedPaintDC dc(this);        
        wxDCPenChanger penChanger(dc, *wxRED_PEN);
        wxDCTextColourChanger textChanger(dc, *wxBLUE);        
                
        dc.SetBackground(*wxWHITE_BRUSH);
        dc.Clear();        

        dc.DrawLine(wxPoint(center.x, 0), wxPoint(center.x, size.GetHeight()));        
        dc.DrawLine(wxPoint(0, center.y), wxPoint(size.GetWidth(), center.y));        
        
        dc.DrawLabel(wxString::Format("Button clicks: %d", m_clicks),
            GetClientRect(), wxALIGN_CENTER_VERTICAL | wxALIGN_CENTER);       
    }       
};


class MyFrame : public wxFrame
{
public:
    MyFrame()
        : wxFrame(NULL, wxID_ANY, _("Test"))
    {
        wxBoxSizer* bSizer = new wxBoxSizer(wxVERTICAL);

        m_canvas = new MyCanvas(this);
        bSizer->Add(m_canvas, 1, wxEXPAND, 0);

        wxButton* button = new wxButton(this, wxID_ANY, "Click me");
        bSizer->Add(button, 0, wxEXPAND | wxALL, 5);
        button->Bind(wxEVT_COMMAND_BUTTON_CLICKED, &MyFrame::OnButtonClicked, this);

        SetSizer(bSizer);
    }	
private:
    MyCanvas* m_canvas;
    
    void OnButtonClicked(wxCommandEvent&)
    {
        m_canvas->IncreaseClicks();
    }
};

class MyApp : public wxApp
{
public:
    virtual bool OnInit()
    {
        (new MyFrame())->Show();
        return true;
    }
}; wxIMPLEMENT_APP(MyApp);
dkaip
Super wx Problem Solver
Super wx Problem Solver
Posts: 334
Joined: Wed Jan 20, 2010 1:15 pm

Re: Strange behaviour in DC

Post by dkaip »

Thank you very much.
I am trying the wxWidgets/samples/drawing/drawing.cpp file.
Compiling says at line 148 (return m_renderer->GetName() == name;) that there is not method GetName in class wxGraphicsRenderer, and at http://docs.wxwidgets.org/3.0.4/classwx ... derer.html i can not find any method like this.
Is wrong the sable or i missing something?
Thanks Jim.
Attachments
drawing.cpp
(79.45 KiB) Downloaded 59 times
PB
Part Of The Furniture
Part Of The Furniture
Posts: 4204
Joined: Sun Jan 03, 2010 5:45 pm

Re: Strange behaviour in DC

Post by PB »

The method was added in 3.1 but as you have wxWidgets 3.1 this should not be an issue?

Are you sure you are not somehow mixing two wxWidgets versions?

BTW, the drawing sample, just as some other ones, can be a bit overwhelming to learn from. It is perhaps better used for demonstrating what can be done instead of how to best do it...
dkaip
Super wx Problem Solver
Super wx Problem Solver
Posts: 334
Joined: Wed Jan 20, 2010 1:15 pm

Re: Strange behaviour in DC

Post by dkaip »

Oh i have wxWidgets-3.0.3. Taking from there drawing runs ok.
Thank's i will study your example.
Jim
dkaip
Super wx Problem Solver
Super wx Problem Solver
Posts: 334
Joined: Wed Jan 20, 2010 1:15 pm

Re: Strange behaviour in DC

Post by dkaip »

I am trying an ImageDialog from image from wxDialog so in the m_staticText1 PaintDC i will have draw lines etc.
I must have the EVT_MOUSE_CAPTURE_LOST event, but don't works neither EVT_MOUSE_CAPTURE_LOST(ImageDialog:: .. neither Bind(wxEVT_MOUSE_CAPTURE_LOST ...
I can not know how to to take effect this. Any suggestion for this way?

Code: Select all

BEGIN_EVENT_TABLE(ImageDialog, Image)
    EVT_MOUSE_CAPTURE_LOST(ImageDialog::OnMouseCaptureLost) // DONT WORKS
END_EVENT_TABLE()

class Image : public wxDialog 
{
	private:
	
	protected:
		
		wxStaticBitmap* m_bitmap1;
		
		// Virtual event handlers, overide them in your derived class
		virtual void OnClose( wxCloseEvent& event ) { event.Skip(); }
		virtual void onMouseEvent( wxMouseEvent& event ) { event.Skip(); }

............................


class ImageDialog: public Image
{


private:
wxImage image;
....


//ctor
...
     Bind(wxEVT_MOUSE_CAPTURE_LOST, &ImageDialog::OnMouseCaptureLost, this); // DONT WORKS ...
-----------------------
../src/common/wincmn.cpp(3346): assert "Assert failure" failed in DoNotifyWindowAboutCaptureLost(): window that captured the mouse didn't process wxEVT_MOUSE_CAPTURE_LOST
Last edited by dkaip on Thu May 10, 2018 2:45 pm, edited 3 times in total.
User avatar
doublemax
Moderator
Moderator
Posts: 19162
Joined: Fri Apr 21, 2006 8:03 pm
Location: $FCE2

Re: Strange behaviour in DC

Post by doublemax »

For which window to you call CaptureMouse()? Probably for a sub-window. That's the one where you need to catch the EVT_MOUSE_CAPTURE_LOST event.
Use the source, Luke!
dkaip
Super wx Problem Solver
Super wx Problem Solver
Posts: 334
Joined: Wed Jan 20, 2010 1:15 pm

Re: Strange behaviour in DC

Post by dkaip »

Sorry, a wrong input..

Code: Select all

. wxImage image is in ImageDialog..
But in wxStaticBitmap mouse events have not EVT_MOUSE_CAPTURE_LOST, only i must do something i suppose in OnMouseEvents as you can see in wxFormBuilder.

Code: Select all

class Image : public wxDialog 
{
	private:
	
	protected:
		
		wxStaticBitmap* m_bitmap1;
		
		// Virtual event handlers, overide them in your derived class
		virtual void OnClose( wxCloseEvent& event ) { event.Skip(); }
		virtual void onMouseEvent( wxMouseEvent& event ) { event.Skip(); }

............................


class ImageDialog: public Image
{


private:
wxImage image;
....
Attachments
33.png
33.png (10.07 KiB) Viewed 2294 times
New Pagodi
Super wx Problem Solver
Super wx Problem Solver
Posts: 469
Joined: Tue Jun 20, 2006 6:47 pm
Contact:

Re: Strange behaviour in DC

Post by New Pagodi »

EVT_MOUSE_CAPTURE_LOST is not a mouse event. It is generated by the wxWindow class.

However, it's a more obscure event, so it is not listed in wxFormbuilder with the other window events. To handle that event, you should use the Connect or Bind methods to add a handler for it in the constructor for your form's derived class.
dkaip
Super wx Problem Solver
Super wx Problem Solver
Posts: 334
Joined: Wed Jan 20, 2010 1:15 pm

Re: Strange behaviour in DC

Post by dkaip »

All that i am trying but no call OnMouseCaptureLost.

Code: Select all

BEGIN_EVENT_TABLE(ImageDialog, Image)
    EVT_MOUSE_CAPTURE_LOST(ImageDialog::OnMouseCaptureLost) // DONT WORKS
END_EVENT_TABLE()
and with

Code: Select all

Bind(wxEVT_MOUSE_CAPTURE_LOST, &ImageDialog::OnMouseCaptureLost, this); // DONT WORKS ...

void ImageDialog::OnMouseCaptureLost( wxMouseEvent& event )
{
    dragging = false;
    event.Skip();
}
PB
Part Of The Furniture
Part Of The Furniture
Posts: 4204
Joined: Sun Jan 03, 2010 5:45 pm

Re: Strange behaviour in DC

Post by PB »

It seems that wxFormBuilder does not support wxMouseCaptureLostEvent. When you check the "OnMouseEvents", it just binds all the mouse events listed in the wxMouseEvent list. wxMouseCaptureLostEvent is different from wxMouseEvent. You need to Bind the event by yourself.

Nevertheless, I just checked that wxMouseCaptureLostEvent works even in a modal dialog, which I was not entirely sure about (the drawing code is just for testing, it is horribly inefficient):

Code: Select all

#include <wx/wx.h>
#include <wx/dcbuffer.h>

#include <vector>

class MyCanvas : public wxPanel
{
public:
    MyCanvas(wxWindow* parent)
        : wxPanel(parent, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxFULL_REPAINT_ON_RESIZE)
    {        
        Bind(wxEVT_LEFT_DOWN, &MyCanvas::OnMouseLeftDown, this);
        Bind(wxEVT_LEFT_UP, &MyCanvas::OnMouseLeftUp, this);
        Bind(wxEVT_MOTION, &MyCanvas::OnMouseMotion, this);
        Bind(wxEVT_MOUSE_CAPTURE_LOST, &MyCanvas::OnMouseCaptureLost, this);

        SetBackgroundStyle(wxBG_STYLE_PAINT);
        Bind(wxEVT_PAINT, &MyCanvas::OnPaint, this);     
    }

    void ClearDrawing()
    {
        m_lines.clear();

        Refresh();
        Update();
    }
    
private:    
    typedef std::vector<wxPoint> Line;
    typedef std::vector<Line> Lines;

    Lines m_lines;

    void OnMouseLeftDown(wxMouseEvent& evt)
    {
        CaptureMouse();
        wxLogMessage("Mouse captured");

        m_lines.push_back(Line());
        AddPoint(evt.GetPosition());        
    }

    void OnMouseLeftUp(wxMouseEvent& evt)
    {
        if ( HasCapture() )
        {
            ReleaseMouse();
            wxLogMessage("Mouse released");                        
        }
    }

    void OnMouseMotion(wxMouseEvent& evt)
    {
        if ( HasCapture() && evt.Dragging() && evt.LeftIsDown() )         
            AddPoint(evt.GetPosition());                     
    }

    void OnMouseCaptureLost(wxMouseCaptureLostEvent& evt)
    {
        wxLogMessage("Mouse capture lost");            
    }
               
    void OnPaint(wxPaintEvent&) 
    { 
        wxAutoBufferedPaintDC dc(this);        
        wxDCPenChanger penChanger(dc, *wxRED_PEN);               
                
        dc.SetBackground(*wxWHITE_BRUSH);
        dc.Clear();   

        for ( size_t i = 0; i < m_lines.size(); ++i )
        {            
            const Line& line = m_lines[i];
            
            dc.DrawLines(line.size(), &line[0]);        
        }
    }       

    void AddPoint(const wxPoint& point)
    {        
        m_lines.back().push_back(point);

        Refresh();
        Update();     
    }
};


class MyDialog : public wxDialog
{
public:
    MyDialog()
        : wxDialog(NULL, wxID_ANY, _("Test"), wxDefaultPosition, wxSize(800, 600))
    {
        wxBoxSizer* bSizer = new wxBoxSizer(wxVERTICAL);

        m_canvas = new MyCanvas(this);
        bSizer->Add(m_canvas, 3, wxEXPAND, 0);

        wxButton* button = new wxButton(this, wxID_ANY, "Clear drawing");
        bSizer->Add(button, 0, wxEXPAND | wxALL, 5);
        button->Bind(wxEVT_COMMAND_BUTTON_CLICKED, &MyDialog::OnClearDrawing, this);

        wxTextCtrl* logCtrl = new wxTextCtrl(this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxTE_MULTILINE | wxTE_READONLY | wxTE_RICH2);
        bSizer->Add(logCtrl, 1, wxEXPAND | wxALL, 5);
        wxLog::DisableTimestamp();
        wxLog::SetActiveTarget(new wxLogTextCtrl(logCtrl));    

        SetSizer(bSizer);
    }	
private:
    MyCanvas* m_canvas;
    
    void OnClearDrawing(wxCommandEvent&)
    {    
        m_canvas->ClearDrawing();
    }
};

class MyApp : public wxApp
{
public:
    virtual bool OnInit()
    {
        MyDialog().ShowModal();
        return false;
    }
}; wxIMPLEMENT_APP(MyApp);
BTW, it would be great if you put separate issues into separate threads. Capturing the mouse is probably not releated to drawing with wxDC...
Last edited by PB on Thu May 10, 2018 3:12 pm, edited 1 time in total.
PB
Part Of The Furniture
Part Of The Furniture
Posts: 4204
Joined: Sun Jan 03, 2010 5:45 pm

Re: Strange behaviour in DC

Post by PB »

dkaip wrote:

Code: Select all

Bind(wxEVT_MOUSE_CAPTURE_LOST, &ImageDialog::OnMouseCaptureLost, this);
As doublemax wrote above, you need to call Bind on the window that captures the mouse. In your case it is proably not a dialog but one of its child controls, such as wxStaticBitmap:

Code: Select all

childControl->Bind(wxEVT_MOUSE_CAPTURE_LOST, &ImageDialog::OnMouseCaptureLost, this);
New Pagodi
Super wx Problem Solver
Super wx Problem Solver
Posts: 469
Joined: Tue Jun 20, 2006 6:47 pm
Contact:

Re: Strange behaviour in DC

Post by New Pagodi »

You need to bind the event handler directly to the control like so:

Code: Select all

m_staticBitmap->Bind(wxEVT_MOUSE_CAPTURE_LOST, &ImageDialog::OnMouseCaptureLost, this);
Replace 'm_staticBitmap' with the name of the control you're actually using.


Also, the event type that the event handler will receive is wxMouseCaptureLostEvent, so the method should look something like this:

Code: Select all

void ImageDialog::OnMouseCaptureLost( wxMouseCaptureLostEvent& event )
{
    dragging = false;
    event.Skip();
}

edit: I see PB answered this while I was typing. Sorry about the double post.
Post Reply