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
Filthy Rich wx Solver
Filthy Rich wx Solver
Posts: 215
Joined: Wed Jan 20, 2010 1:15 pm

Strange behaviour in DC

Post by dkaip » Mon May 07, 2018 8:42 am

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: 2025
Joined: Sun Jan 03, 2010 5:45 pm

Re: Strange behaviour in DC

Post by PB » Mon May 07, 2018 9:08 am

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
Filthy Rich wx Solver
Filthy Rich wx Solver
Posts: 215
Joined: Wed Jan 20, 2010 1:15 pm

Re: Strange behaviour in DC

Post by dkaip » Mon May 07, 2018 3:16 pm

Have some url fo some basic drawing tutorials?
Thank's
Jim

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

Re: Strange behaviour in DC

Post by PB » Mon May 07, 2018 3:45 pm

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
Filthy Rich wx Solver
Filthy Rich wx Solver
Posts: 215
Joined: Wed Jan 20, 2010 1:15 pm

Re: Strange behaviour in DC

Post by dkaip » Mon May 07, 2018 8:40 pm

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

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

Re: Strange behaviour in DC

Post by PB » Mon May 07, 2018 8:56 pm

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
Filthy Rich wx Solver
Filthy Rich wx Solver
Posts: 215
Joined: Wed Jan 20, 2010 1:15 pm

Re: Strange behaviour in DC

Post by dkaip » Mon May 07, 2018 9:25 pm

Oh i have wxWidgets-3.0.3. Taking from there drawing runs ok.
Thank's i will study your example.
Jim

dkaip
Filthy Rich wx Solver
Filthy Rich wx Solver
Posts: 215
Joined: Wed Jan 20, 2010 1:15 pm

Re: Strange behaviour in DC

Post by dkaip » Thu May 10, 2018 12:22 pm

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: 13990
Joined: Fri Apr 21, 2006 8:03 pm
Location: $FCE2

Re: Strange behaviour in DC

Post by doublemax » Thu May 10, 2018 1:34 pm

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
Filthy Rich wx Solver
Filthy Rich wx Solver
Posts: 215
Joined: Wed Jan 20, 2010 1:15 pm

Re: Strange behaviour in DC

Post by dkaip » Thu May 10, 2018 2:29 pm

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

New Pagodi
Super wx Problem Solver
Super wx Problem Solver
Posts: 307
Joined: Tue Jun 20, 2006 6:47 pm
Contact:

Re: Strange behaviour in DC

Post by New Pagodi » Thu May 10, 2018 2:54 pm

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
Filthy Rich wx Solver
Filthy Rich wx Solver
Posts: 215
Joined: Wed Jan 20, 2010 1:15 pm

Re: Strange behaviour in DC

Post by dkaip » Thu May 10, 2018 3:08 pm

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: 2025
Joined: Sun Jan 03, 2010 5:45 pm

Re: Strange behaviour in DC

Post by PB » Thu May 10, 2018 3:09 pm

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: 2025
Joined: Sun Jan 03, 2010 5:45 pm

Re: Strange behaviour in DC

Post by PB » Thu May 10, 2018 3:11 pm

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: 307
Joined: Tue Jun 20, 2006 6:47 pm
Contact:

Re: Strange behaviour in DC

Post by New Pagodi » Thu May 10, 2018 3:14 pm

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