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

Re: Strange behaviour in DC

Post by dkaip »

Ok thank's. As you can see i have modal dialog, class Image derive from wxDialog.
Code is simple. Because i must have modal dialog over another modal dialog, ImageDialog stops an in shut-down says ..
../src/common/wincmn.cpp(3346): assert "Assert failure" failed in DoNotifyWindowAboutCaptureLost(): window that captured the mouse didn't process wxEVT_MOUSE_CAPTURE_LOST

Code: Select all

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

///////////////////////////////////////////////////////////////////////////////
/// Class Image
///////////////////////////////////////////////////////////////////////////////
class Image : public wxDialog
{
	private:

	protected:
		enum
		{
			idBtnAbout = 1000,
			idBtnQuit
		};

		wxBitmapButton* m_bpButton1;
		wxBitmapButton* m_bpButton2;
		wxBitmapButton* m_bpButton3;
		wxBitmapButton* m_bpButton4;
		wxBitmapButton* m_bpButton5;
		wxBitmapButton* m_bpButton6;
		wxBitmapButton* m_bpButton9;
		wxBitmapButton* m_bpButton10;
		wxBitmapButton* m_bpButton11;
		wxBitmapButton* m_bpButton12;
		wxBitmapButton* m_bpButton13;
		wxStaticBitmap* m_bitmap1;
		wxStaticLine* m_staticline1;
		wxButton* BtnAbout;
		wxButton* BtnQuit;

		// Virtual event handlers, overide them in your derived class
		virtual void OnClose( wxCloseEvent& event ) { event.Skip(); }
		virtual void OnDoubleClick( wxMouseEvent& event ) { event.Skip(); }
		virtual void OnMouseDown( wxMouseEvent& event ) { event.Skip(); }
		virtual void OnMouseUp( wxMouseEvent& event ) { event.Skip(); }
		virtual void OnMouseMove( wxMouseEvent& event ) { event.Skip(); }
		virtual void OnAbout( wxCommandEvent& event ) { event.Skip(); }
		virtual void OnQuit( wxCommandEvent& event ) { event.Skip(); }


	public:

		Image( wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = wxEmptyString, const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxSize( 1126,687 ), long style = wxDEFAULT_DIALOG_STYLE );
		~Image();

};




class ImageDialog: public Image
{
public:
    ImageDialog(wxDialog *dlg,inputfile& file);
    ~ImageDialog();
private:
    virtual void OnClose(wxCloseEvent& event);
    virtual void OnQuit(wxCommandEvent& event);
    virtual void OnAbout(wxCommandEvent& event);
    wxBitmap CreateColorBitmap(const wxColour& c);

    virtual void OnZoomIn(wxCommandEvent& event);
    virtual void OnZoomOut(wxCommandEvent& event);


    virtual void OnDoubleClick( wxMouseEvent& event );
    virtual void OnMouseDown( wxMouseEvent& event );
    virtual void OnMouseUp( wxMouseEvent& event );
    virtual void OnMouseMove( wxMouseEvent& event );

    void OnSizeChanged(wxSizeEvent& event);

    bool dragging;

    int BmpDX,BmpDY,ScreenDX,ScreenDY;
    float fa;

    void SetZoom(float factor);
    float GetZoom();
    wxImage image;
    void UpdateBitmap();

    void OnKeyDown(wxKeyEvent& event);

};
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 try it. No response from OnMouseCaptureLost.



Code: Select all


m_bitmap1->Bind(wxEVT_MOUSE_CAPTURE_LOST, &ImageDialog::OnMouseCaptureLost, this);


void ImageDialog::OnMouseCaptureLost( wxMouseCaptureLostEvent& event )
{
wxMessageBox("Mouse Capture Lost");
    dragging = false;
    event.Skip();
}
User avatar
doublemax
Moderator
Moderator
Posts: 19114
Joined: Fri Apr 21, 2006 8:03 pm
Location: $FCE2

Re: Strange behaviour in DC

Post by doublemax »

Find the place where CaptureMouse() gets called and show the code in context (complete method).
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 »

PB your code work just fine. But my problem in drawing in a dc is that i dont know how to draw a line before any other processes.
For example if change OnClearDrawing like this a line appears if push the button.

Code: Select all

    void OnClearDrawing(wxCommandEvent&)
    {   
        m_canvas->ClearDrawing();
        wxPaintDC dc(m_canvas);
        dc.SetPen( wxPen( wxColour( 128, 0, 0 ), 3 ));
        dc.DrawLine(0,0,200,200);
    }

But if i want line before any paint process maybe i must put the draw line in constructor. But no line appears.
Where i must put the drawline process for appear line on startup of program and before any other process?
All i must do is at strart up of the program open a picture and showing immediately a number of shapes before any human active.

Code: Select all

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

        wxPaintDC dc(this);
        dc.SetPen( wxPen( wxColour( 128, 0, 0 ), 3 ));
        dc.DrawLine(0,0,200,200);

    }

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

Re: Strange behaviour in DC

Post by PB »

dkaip wrote:But if i want line before any paint process maybe i must put the draw line in constructor. But no line appears. here i must put the drawline process for appear line on startup of program and before any other process?
All i must do is at strart up of the program open a picture and showing immediately a number of shapes before any human active.
As I told you before and showed in the code I posted, you must be able to draw everything needed from the paint event handler. If you draw something only elsewhere, it gets overdrawn by what you draw in the paint event handler. Paint events are generated either by you, calling Refresh+Update or the OS whenever a window need to be redrawn for some reason (resized, not overlapped by other window anymore, etc.). All this was shown also in the code I posted, where one can draw lines with a mouse and you made an effort to change it so it does not work?

If in the line drawing sample you want to have one line initially, you add it (i.e., store the point coordinates) but do not draw it. It will get drawn in the paint event

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);     
        
        // this line will always be drawn
        m_defaultLine.push_back(wxPoint (5, 5));
        m_defaultLine.push_back(wxPoint (150, 150));        
    }

    void ClearDrawing()
    {
        m_userLines.clear();        

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

    Line  m_defaultLine;
    Lines m_userLines;

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

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

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

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

    void OnMouseCaptureLost(wxMouseCaptureLostEvent&)
    {
        wxLogMessage("Mouse capture lost");            
    }
               
    void OnPaint(wxPaintEvent&) 
    { 
        static size_t evtCount = 0;
        
        wxLogMessage("Handling paint event no. %zu", ++evtCount);
        
        wxAutoBufferedPaintDC dc(this);                
                
        dc.SetBackground(*wxWHITE_BRUSH);
        dc.Clear();   

        // draw the line which is always part of the drawing
        wxDCPenChanger lineDefaultPenChanger(dc, *wxBLUE_PEN);               
        
        dc.DrawLines(m_defaultLine.size(), &m_defaultLine[0]);
        

        // draw lines the user created
        wxDCPenChanger lineUserPenChanger(dc, *wxRED_PEN);

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

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

        Refresh();
        Update();     
    }
};


class MyDialog : public wxDialog
{
public:
    MyDialog()
        : wxDialog(NULL, wxID_ANY, _("Test"), wxDefaultPosition, wxSize(800, 600), wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER)
    {
        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 user drawn lines");
        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::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);
I advise you to compile and run the unchanged code from above to see how it works and then read the code to understand it. Please notice that all the drawing is done from the paint event handler. When something changes (a point is added, the drawing should be cleared), the data upon which the drawing is based are updated and redrawing is done by triggering the paint event by calling Update+Refresh.

You should also pay attention to other things related to drawing I wrote, such a creating wxPaintEvent only in the paint event handler or not leaving non-stock GDI objects selected in a wxDC when it gets destroyed...

It really is simple.

Edit: Edited the code to show more.
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 »

PB thank's very much. Very good explanation and now i understand how i must design.
Putting a bitmap as background, i must over it to design shape.
My line goes under bitmap. Any help most welcome.
Thank's again.
Jim
PB
Part Of The Furniture
Part Of The Furniture
Posts: 4193
Joined: Sun Jan 03, 2010 5:45 pm

Re: Strange behaviour in DC

Post by PB »

dkaip wrote:Putting a bitmap as background, i must over it to design shape.
My line goes under bitmap.
Sorry, I assume there is a question but I do not understand 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 »

Hello. I mean as you can see in picture, the lines do not go through image.

Code: Select all

wxBitmap orig_bitmap=wxBitmap("005.png");
wxImage orig_image=orig_bitmap.ConvertToImage();

int new_w = int(orig_image.GetWidth() * 0.5);
int new_h = int(orig_image.GetHeight() * 0.5);

wxImage scaled =orig_image.Scale(new_w,new_h,wxIMAGE_QUALITY_NORMAL);
bitmap1->SetBitmap(wxBitmap(scaled));

wxPaintDC dc(bitmap1);
for(int i=0; i<2000; i+=50)dc.DrawLine(0,i,2000,i);
for(int i=0; i<2000; i+=50)dc.DrawLine(i,0,i,2000);

wxMemoryDC dc1;
dc1.SelectObject(orig_bitmap);
dc1.DrawLine(0,0,2000,2000);
Attachments
040.png
040.png (12.25 KiB) Viewed 2148 times
PB
Part Of The Furniture
Part Of The Furniture
Posts: 4193
Joined: Sun Jan 03, 2010 5:45 pm

Re: Strange behaviour in DC

Post by PB »

If you want the grid to be visible over the bitmap, you need to either:
1) Draw it after you drew the bitmap.
2) The bitmap needs to have transparency (either via alpha or mask).

You should construct wxPaintDC with a wxWindow* pointing to a window you are trying to draw as a parameter.

BTW, the drawing code you posted would be very inefficient to use in the real application. E.g., you would load the bitmap from the file only once and not in every redraw; similarly, you would scale it just once when needed and then use the cached scaled bitmap since then. Moreover, you do not need to use wxMemoryDC, you can draw a wxBitmap with wxDC::DrawBitmap(). TBH, I find the code you posted somewhat confusing...
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 »

This is not a program it is just a test to know how to draw these lines over a bitmap.
As you see first construct is bitmap and second the lines. Maybe must work with mask dc, but how?
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 »

In test program bellow from wiki page in a DC draw shapes.
But if i put an image with code ...

Code: Select all

    
BasicDrawPane::BasicDrawPane(wxFrame* parent) :
    wxPanel(parent)
{

    bitmap = new wxStaticBitmap( this, wxID_ANY, wxNullBitmap,wxDefaultPosition, wxDefaultSize, 0 );
    wxBitmap bmp("005.png");
    wxImage img=bmp.ConvertToImage();
    int w = int(bmp.GetWidth() * 0.5);
    int h = int(bmp.GetHeight() * 0.5);
    wxImage scaled =img.Scale(w,h,wxIMAGE_QUALITY_NORMAL);
    bitmap->SetBitmap(wxBitmap(scaled));
}

I take second image without shapes.
How i must draw shapes over a bitmap as background?
Hello wxDC_041.png
Hello wxDC_041.png (12.48 KiB) Viewed 2099 times
Hello wxDC_040.png
Hello wxDC_040.png (6.32 KiB) Viewed 2099 times

Code: Select all

#include "wx/wx.h"
#include "wx/sizer.h"
#include "wx/bitmap.h"
#include <wx/statbmp.h>

class BasicDrawPane : public wxPanel
{

public:
    BasicDrawPane(wxFrame* parent);

    wxStaticBitmap* bitmap;

    void paintEvent(wxPaintEvent & evt);
    void paintNow();

    void render(wxDC& dc);

    DECLARE_EVENT_TABLE()
};


class MyApp: public wxApp
{
    bool OnInit();

    wxFrame *frame;
    BasicDrawPane * drawPane;
public:

};

IMPLEMENT_APP(MyApp)

bool MyApp::OnInit()
{
    wxBoxSizer* sizer = new wxBoxSizer(wxHORIZONTAL);
    frame = new wxFrame((wxFrame *)NULL, -1,  wxT("Hello wxDC"), wxPoint(50,50), wxSize(800,600));

    drawPane = new BasicDrawPane( (wxFrame*) frame );
    sizer->Add(drawPane, 1, wxEXPAND);

    frame->SetSizer(sizer);
    frame->SetAutoLayout(true);

    frame->Show();
    return true;
}

BEGIN_EVENT_TABLE(BasicDrawPane, wxPanel)
    EVT_PAINT(BasicDrawPane::paintEvent)
END_EVENT_TABLE()


BasicDrawPane::BasicDrawPane(wxFrame* parent) :
    wxPanel(parent)
{

}


void BasicDrawPane::paintEvent(wxPaintEvent & evt)
{
    wxPaintDC dc(this);
    render(dc);
}

void BasicDrawPane::paintNow()
{
    wxClientDC dc(this);
    render(dc);
}

void BasicDrawPane::render(wxDC&  dc)
{
    // draw some text
    dc.DrawText(wxT("Testing"), 40, 60);

    // draw a circle
    dc.SetBrush(*wxGREEN_BRUSH); // green filling
    dc.SetPen( wxPen( wxColor(255,0,0), 5 ) ); // 5-pixels-thick red outline
    dc.DrawCircle( wxPoint(200,100), 25 /* radius */ );

    // draw a rectangle
    dc.SetBrush(*wxBLUE_BRUSH); // blue filling
    dc.SetPen( wxPen( wxColor(255,175,175), 10 ) ); // 10-pixels-thick pink outline
    dc.DrawRectangle( 300, 100, 400, 200 );

    // draw a line
    dc.SetPen( wxPen( wxColor(0,0,0), 3 ) ); // black line, 3 pixels thick
    dc.DrawLine( 300, 100, 700, 300 ); // draw line across the rectangle
}
User avatar
doublemax
Moderator
Moderator
Posts: 19114
Joined: Fri Apr 21, 2006 8:03 pm
Location: $FCE2

Re: Strange behaviour in DC

Post by doublemax »

Code: Select all

 bitmap = new wxStaticBitmap( this, wxID_ANY, wxNullBitmap,wxDefaultPosition, wxDefaultSize, 0 );
Don't use a wxStaticBitmap here. Just draw the bitmap yourself in the render method.
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 »

Thank's doublemax. Finally the reason is that must not have wxStaticImage control.

Code: Select all

void BasicDrawPane::render(wxDC&  dc)
{
    wxBitmap bmp("5.png");
    wxImage img=bmp.ConvertToImage();
    int w = int(bmp.GetWidth() * 0.5);
    int h = int(bmp.GetHeight() * 0.5);
    wxImage scaled =img.Scale(w,h,wxIMAGE_QUALITY_NORMAL);
//    bitmap->SetBitmap(wxBitmap(scaled));
    dc.SetClippingRegion( 0,0,2000,2000 );
    dc.DrawBitmap(bmp,0,0,false);
Post Reply