Page 1 of 2

Strange behaviour in DC

Posted: Mon May 07, 2018 8:42 am
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);
}

Re: Strange behaviour in DC

Posted: Mon May 07, 2018 9:08 am
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.

Re: Strange behaviour in DC

Posted: Mon May 07, 2018 3:16 pm
by dkaip
Have some url fo some basic drawing tutorials?
Thank's
Jim

Re: Strange behaviour in DC

Posted: Mon May 07, 2018 3:45 pm
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);

Re: Strange behaviour in DC

Posted: Mon May 07, 2018 8:40 pm
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.

Re: Strange behaviour in DC

Posted: Mon May 07, 2018 8:56 pm
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...

Re: Strange behaviour in DC

Posted: Mon May 07, 2018 9:25 pm
by dkaip
Oh i have wxWidgets-3.0.3. Taking from there drawing runs ok.
Thank's i will study your example.
Jim

Re: Strange behaviour in DC

Posted: Thu May 10, 2018 12:22 pm
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

Re: Strange behaviour in DC

Posted: Thu May 10, 2018 1:34 pm
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.

Re: Strange behaviour in DC

Posted: Thu May 10, 2018 2:29 pm
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;
....

Re: Strange behaviour in DC

Posted: Thu May 10, 2018 2:54 pm
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.

Re: Strange behaviour in DC

Posted: Thu May 10, 2018 3:08 pm
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();
}

Re: Strange behaviour in DC

Posted: Thu May 10, 2018 3:09 pm
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...

Re: Strange behaviour in DC

Posted: Thu May 10, 2018 3:11 pm
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);

Re: Strange behaviour in DC

Posted: Thu May 10, 2018 3:14 pm
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.