Page 1 of 1

wxGraphicsContex Text Drawing

Posted: Wed Aug 30, 2017 5:14 am
by sam_des
Hello,

I am writing a custom control & using & wxBufferedPaintDC & wxGraphicsContext for it. I am able to draw rectangles, lines, circle etc. & its works as expected. But unable to draw the text. Nothing appears at all.
I am using wxWidgets 3.0.3 & Code::Blocks/mingw

Code: Select all

void PlotWidget::DrawBackground( wxBufferedDC* dc )
{
    wxGraphicsContext* gc = wxGraphicsContext::Create( *dc );

    wxRect rc = GetClientRect();
    
    gc->SetBrush( wxBrush(m_WidgetBkColor) );
    gc->SetPen( wxPen(m_WidgetBkColor, 1, wxSOLID) );
    gc->DrawRectangle( rc.x, rc.y, rc.width, rc.height );
    // This works as expected

    gc->SetFont( m_PlotLabelFont, m_PlotLabelColor ); 
    // these are wxFont & wxColour class members, initialized elsewhere
    // Creating a font & colour explicitly here also doesn't work either

    gc->DrawText( _("SOME TEXT"), rc.width/2, rc.height/2 );

    delete gc;
}
I also tried the same code with wxPython 4.0.0b1 which works just fine.
If I disable wxGraphicsContext with C++, everything again works fine, including drawing text.

Any help would be highly appreciated.
TIA,
sam_des

Re: wxGraphicsContex Text Drawing

Posted: Wed Aug 30, 2017 10:13 am
by doublemax
There isn't much you can do wrong when drawing text. I suspect the text has the same color as the background. Try drawing at a different postion and / or a different color for a test.

And check that the font is valid with wxFont::IsOk()

BTW: I'm not 100% if this is the right thing to do:

Code: Select all

wxRect rc = GetClientRect();
What values do you get for rc.x and rc.y? They should be zero. If they're not, use this instead:

Code: Select all

wxRect rc( GetClientSize() );

Re: wxGraphicsContex Text Drawing

Posted: Wed Aug 30, 2017 3:12 pm
by sam_des
Hello,

Tried following things,
1) wxFont is valid
2) Created separate font in DrawBackground() itself, explicitly specifying different colour.
3) wxRect rc( GetClientSize() ) instead of wxRect rc = GetClientRect(). Although I do get correct x,y,h,w with original code too.
4) Disabled the background painting with gc->DrawRectangle(). Now only thing being done in DrawBackground() is drawing text.
5) Tried different positions to draw the text with different strings.

Nothing appears at all.

sam_des

Re: wxGraphicsContex Text Drawing

Posted: Wed Aug 30, 2017 4:19 pm
by PB
FWIW, I have tried this (wxWidgets trunk, Windows 10), which is pretty much the same as the code you posted

Code: Select all

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

class MyCanvas : public wxPanel
{
public:
    MyCanvas(wxWindow* parent)
        : wxPanel(parent, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxFULL_REPAINT_ON_RESIZE)
    {       
        SetBackgroundStyle(wxBG_STYLE_PAINT);
        Bind(wxEVT_PAINT, &MyCanvas::OnPaint, this);               
    }
private:   
    void OnPaint(wxPaintEvent&)
    {
        wxBufferedPaintDC dc(this);
        wxGraphicsContext* gc = wxGraphicsContext::Create(dc);
        wxRect rc = GetClientRect();
        wxColour bkColor(*wxBLACK);
        wxColour textColor(*wxGREEN);
        wxFont font(10, wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_BOLD); 

        gc->SetBrush(wxBrush(bkColor) );
        gc->SetPen(wxPen(bkColor, 1, wxPENSTYLE_SOLID));
        gc->DrawRectangle(rc.x, rc.y, rc.width, rc.height);

        gc->SetFont(font, textColor);     
        gc->DrawText( _("SOME TEXT"), rc.width/2, rc.height/2 );

        delete gc;
    }
};

class MyApp : public wxApp
{
public:
    virtual bool OnInit()
    {     
        wxFrame* f = new wxFrame(NULL, wxID_ANY, _("Test"));
        new MyCanvas(f);
        f->Show();
        return true;
    }
}; wxIMPLEMENT_APP(MyApp);
and it works as expected for me:
wxgc.png
wxgc.png (2.3 KiB) Viewed 8472 times
Assuming m_PlotLabelColor.IsOk() returns true, the only thing that comes to my mind, and I do not even know if it is possible, is that your font cannot be used with wxGC. However unlikely, does this fail:

Code: Select all

bool gfFontError = gc->CreateFont(m_PlotLabelFont, m_PlotLabelColor).IsNull();

Re: wxGraphicsContex Text Drawing

Posted: Wed Aug 30, 2017 6:03 pm
by sam_des
This is my partial code,

Some definitions,

Code: Select all

wxBitmap                m_Buffer;

m_WidgetBkColor   = wxColor( 200, 200, 200 );
m_PlotBkColor       = wxColor( 255, 255, 255 );
m_PlotLabelColor   = wxColor( 0, 0, 0 );

m_PlotLabelFont     = wxFont( 18, wxSWISS, wxFONTSTYLE_NORMAL, wxNORMAL, false, _("Elephant") );
Actual code,

Code: Select all

void PlotWidget::OnPaint( wxPaintEvent& event )
{
    wxBufferedPaintDC dc( this, m_Buffer );
}

void PlotWidget::RedrawInBuffer()
{
    wxSize sz = GetClientSize();

    m_Buffer.Create( sz.GetWidth(), sz.GetHeight() );

    wxBufferedDC bdc;
    bdc.SelectObject( m_Buffer );
    bdc.Clear();

    DrawBackground( &bdc );
    
    Refresh();
}

void PlotWidget::DrawBackground( wxBufferedDC* dc )
{
   wxGraphicsContext* gc = wxGraphicsContext::Create( *dc );

    wxRect rc = GetClientRect();
    
    gc->SetBrush( wxBrush(m_WidgetBkColor) );
    gc->SetPen( wxPen(m_WidgetBkColor, 1, wxSOLID) );

    gc->DrawRectangle( rc.x, rc.y, rc.width, rc.height );

    gc->SetFont( m_PlotLabelFont, m_PlotLabelColor );

    gc->DrawText( _("SOME TEXT"), rc.width/2, rc.height/2 );
    
    delete gc;
}
Code works as expected when I use, wxBufferedDC instead of wxGraphicsContext. Also GC works ok with drawing lines, rects etc.

_sam_des

Re: wxGraphicsContex Text Drawing

Posted: Wed Aug 30, 2017 7:32 pm
by New Pagodi
That seems to be a little complicated to me. (Also, unless I'm missing something you never seem to draw anything in your paint handler). I would just create a graphics context in the paint handler and draw to it there. Like so:

Code: Select all

// For compilers that support precompilation, includes "wx/wx.h".
#include "wx/wxprec.h"

#ifdef __BORLANDC__
    #pragma hdrstop
#endif

// for all others, include the necessary headers (this file is usually all you
// need because it includes almost all "standard" wxWidgets headers)
#ifndef WX_PRECOMP
    #include "wx/wx.h"
#endif

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

class widget: public wxWindow
{
    public:
        widget(wxWindow *parent, wxWindowID id=wxID_ANY,
                   const wxPoint &pos=wxDefaultPosition,
                   const wxSize &size=wxDefaultSize,
                   long style=0, const wxString &name="widget");

    private:
        void OnPaint( wxPaintEvent& event );

        wxColour m_WidgetBkColor,m_PlotBkColor,m_PlotLabelColor;
        wxFont m_PlotLabelFont;
};

widget::widget(wxWindow *parent, wxWindowID id, const wxPoint &pos,
                       const wxSize &size, long style, const wxString &name)
            :wxWindow(parent, id, pos, size, style|wxFULL_REPAINT_ON_RESIZE, name)
{
    m_WidgetBkColor   = wxColor( 200, 200, 200 );
    m_PlotBkColor       = wxColor( 255, 255, 255 );
    m_PlotLabelColor   = wxColor( 0, 0, 0 );
    m_PlotLabelFont     = wxFont( 18, wxSWISS, wxFONTSTYLE_NORMAL, wxNORMAL, false, _("Elephant") );

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


void widget::OnPaint( wxPaintEvent& event )
{
    wxAutoBufferedPaintDC dc(this);

    wxGraphicsContext* gc = wxGraphicsContext::Create( dc );

    wxRect rc = GetClientRect();

    gc->SetBrush( wxBrush(m_WidgetBkColor) );
    gc->SetPen( wxPen(m_WidgetBkColor, 1, wxSOLID) );

    gc->DrawRectangle( rc.x, rc.y, rc.width, rc.height );

    gc->SetFont( m_PlotLabelFont, m_PlotLabelColor );

    gc->DrawText( _("SOME TEXT"), rc.width/2, rc.height/2 );

    delete gc;
}

class MyFrame : public wxFrame
{
    public:
        MyFrame( wxWindow* parent, int id = wxID_ANY, wxString title = "Demo"
                , wxPoint pos = wxDefaultPosition, wxSize size = wxDefaultSize
                , int style = wxDEFAULT_FRAME_STYLE|wxTAB_TRAVERSAL );
};


MyFrame::MyFrame( wxWindow* parent, int id, wxString title, wxPoint pos
                 , wxSize size, int style )
        :wxFrame( parent, id, title, pos, size, style )
{
    new widget( this );
}

class MyApp : public wxApp
{
    public:
        virtual bool OnInit()
        {
            MyFrame* frame = new MyFrame(NULL);
            frame->Show();
            return true;
        }
};

wxIMPLEMENT_APP(MyApp);
But I'm guessing the reason you're using a buffer is that the actual drawing will ultimately be much more complicated and time consuming so you want to cache a bitmap and only recompute it when needed. If that's the case, here's one possible way to do that:

Code: Select all

class widget: public wxWindow
{
    public:
        widget(wxWindow *parent, wxWindowID id=wxID_ANY,
                   const wxPoint &pos=wxDefaultPosition,
                   const wxSize &size=wxDefaultSize,
                   long style=0, const wxString &name="widget");

    private:
        void OnPaint( wxPaintEvent& event );
        void OnSize(wxSizeEvent& event);

        wxColour m_WidgetBkColor,m_PlotBkColor,m_PlotLabelColor;
        wxFont m_PlotLabelFont;
        wxBitmap m_Buffer;
        bool m_dirty;
};

widget::widget(wxWindow *parent, wxWindowID id, const wxPoint &pos,
                       const wxSize &size, long style, const wxString &name)
            :wxWindow(parent, id, pos, size, style|wxFULL_REPAINT_ON_RESIZE, name)
{
    m_WidgetBkColor   = wxColor( 200, 200, 200 );
    m_PlotBkColor       = wxColor( 255, 255, 255 );
    m_PlotLabelColor   = wxColor( 0, 0, 0 );
    m_PlotLabelFont     = wxFont( 18, wxSWISS, wxFONTSTYLE_NORMAL, wxNORMAL, false, _("Elephant") );

    SetBackgroundStyle(wxBG_STYLE_PAINT);
    Bind( wxEVT_PAINT, &widget::OnPaint, this );
    Bind( wxEVT_SIZE, &widget::OnSize, this );
}

void widget::OnSize( wxSizeEvent& event )
{
    m_dirty=true;
    event.Skip();
}

void widget::OnPaint( wxPaintEvent& event )
{
    if(m_dirty)
    {
        wxSize sz = GetClientSize();

        m_Buffer = wxBitmap( sz.GetWidth(), sz.GetHeight() );

        wxMemoryDC dc(m_Buffer);

        wxGraphicsContext* gc = wxGraphicsContext::Create( dc );

        wxRect rc = GetClientRect();

        gc->SetBrush( wxBrush(m_WidgetBkColor) );
        gc->SetPen( wxPen(m_WidgetBkColor, 1, wxSOLID) );

        gc->DrawRectangle( rc.x, rc.y, rc.width, rc.height );

        gc->SetFont( m_PlotLabelFont, m_PlotLabelColor );

        gc->DrawText( _("SOME TEXT"), rc.width/2, rc.height/2 );

        delete gc;

        m_dirty=false;
    }

    wxPaintDC dc(this);
    dc.DrawBitmap(m_Buffer,0,0);
}

Re: wxGraphicsContex Text Drawing

Posted: Thu Aug 31, 2017 5:52 pm
by sam_des
Hi,

Drawing is quite complicated & require fast update, so I am using the buffered draw. Everything drawn onto a memory buffer/bitmap & Paint Event simply copies the buffer to screen. Just for testing, I tried drawing text directly from Paint Event, but without success.

To reiterate, everything works fine when I don't use the GraphicsContext, which I require to draw the anti-aliased lines and which I can do, but fail with text. Since my problem is associated only with DrawText(), I've temporarily moved all my TextDrawing functionality to non-GraphicsContext drawing.
But its only partial solution,

_sam_des

Re: wxGraphicsContex Text Drawing

Posted: Thu Aug 31, 2017 6:16 pm
by PB
Did you try what I suggested, i.e., whether the code I posted works for you and whether your wxFont can be is successfully converted into graphic font and with what results?

Seems like you missed posts by NewPagodi and me...

Re: wxGraphicsContex Text Drawing

Posted: Thu Aug 31, 2017 6:21 pm
by iwbnwif
Since my problem is associated only with DrawText(), I've temporarily moved all my TextDrawing functionality to non-GraphicsContext drawing.
Actually, I have found this to be a better solution.

It is possible to draw using both a 'normal' wxDC and a wxGCDC onto the same DC. I found that the text looks better when drawn with the wxDC as opposed to the wxGCDC because in the latter case it appears to be anti-aliased twice (once by the text drawing and again by the graphics context).

Also, if you ever need to drawing orthographic lines such as borders, gridlines etc. it best to use the straightforward DC because you can be sure that they are 1 pixel wide.