Making Background of Custom Widget Transparent

Are you writing your own components and need help with how to set them up or have questions about the components you are deriving from ? Ask them here.
Post Reply
sam_des
Earned a small fee
Earned a small fee
Posts: 11
Joined: Wed Aug 30, 2017 4:55 am

Making Background of Custom Widget Transparent

Post by sam_des » Sun Jul 21, 2019 3:12 pm

Hi,

I have written a simple Custom Widget for LED using wxPanel. It includes nothing more than a circle with user selected On-Color & Off-Color.

Everything works fine, when background of parent widget is white.

As soon as background of parent widget turns dark, a white(or any other color which I set) rectangle appears around the circle.

Is there any way to make my LED widget background transparent or wxPanel non-rectangular?
TIA,
sam
Attachments
led2.png
led2.png (484 Bytes) Viewed 212 times
led1.png
led1.png (488 Bytes) Viewed 212 times

User avatar
doublemax
Moderator
Moderator
Posts: 14098
Joined: Fri Apr 21, 2006 8:03 pm
Location: $FCE2

Re: Making Background of Custom Widget Transparent

Post by doublemax » Mon Jul 22, 2019 3:51 pm

Achieving "real" transparency that e.g. would even work on a parent with a background image, is hard.

For simple cases, just set the background color of the custom widget to the background color of its parent.
Use the source, Luke!

sam_des
Earned a small fee
Earned a small fee
Posts: 11
Joined: Wed Aug 30, 2017 4:55 am

Re: Making Background of Custom Widget Transparent

Post by sam_des » Mon Jul 22, 2019 5:07 pm

Hi,
Thanks for info.
Background of the LED widget will be a bitmap/png/jpeg file, actually a map, loaded by the user on the fly.
So background of LED widget may have multiple colors simultaneously.

With circular LED shape, while background may be tolerable, but there can be Triangular shaped LED and its terribly annoying.

Is there a workaround this?
sam

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

Re: Making Background of Custom Widget Transparent

Post by PB » Mon Jul 22, 2019 7:21 pm

What exactly is the issue?

This naive code

Code: Select all

#include <wx/wx.h>

class BitmapPanel : public wxPanel
{
public:
    BitmapPanel(wxWindow* parent, const wxBitmap& bitmap = wxNullBitmap)
        : wxPanel(parent, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxFULL_REPAINT_ON_RESIZE),
          m_bitmap(bitmap)
    {                
        SetBackgroundStyle(wxBG_STYLE_PAINT);
        Bind(wxEVT_PAINT, &BitmapPanel::OnPaint, this);                        
    }

    void SetBitmap(const wxBitmap& bitmap)
    {
        m_bitmap = bitmap;     
        Update(); Refresh();
    }

private:
    wxBitmap m_bitmap;
    
    void OnPaint(wxPaintEvent&) 
    { 
        wxPaintDC dc(this);
        
        dc.SetBackground(*wxBLACK_BRUSH);
        dc.Clear();

        if ( !m_bitmap.IsOk() )
            return; 
        
        wxMemoryDC mdc(m_bitmap);

        dc.StretchBlit(wxPoint(0, 0), dc.GetSize(), &mdc, wxPoint(0,0), mdc.GetSize(), wxCOPY, true);
    }       
};

class wxLEDCtrl : public wxWindow
{
public:
    enum Shape { Circle, Triangle };
    enum State { Off = 0, On = 1 };
    
    wxLEDCtrl(wxWindow* parent, Shape shape = Circle, State state = On, 
             const wxColour& colourOn = *wxRED, const wxColour& colourOff = *wxLIGHT_GREY)
        : wxWindow(parent, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTRANSPARENT_WINDOW),
          m_shape(shape), m_state(state), m_colourOn(colourOn), m_colourOff(colourOff)
    {               
        SetBackgroundStyle(wxBG_STYLE_PAINT);
        Bind(wxEVT_PAINT, &wxLEDCtrl::OnPaint, this);    
    }

    bool HasTransparentBackground() override { return true; }

protected:        
    wxSize DoGetBestClientSize() const override
    {
        const int size = FromDIP(32);

        return wxSize(size, size);        
    }

private:   
    Shape m_shape;
    State m_state;
    wxColour m_colourOn, m_colourOff;
    
    void OnPaint(wxPaintEvent&)
    {
        const wxColour* clr = m_state == On ? &m_colourOn : &m_colourOff;
        wxPaintDC dc(this);
        wxDCPenChanger penChanger(dc, *clr);
        wxDCBrushChanger brushChanger(dc, *clr);
        const wxRect r = wxRect(dc.GetSize());

        if ( m_shape == Circle )
        {
            dc.DrawCircle(r.GetLeft() + r.GetWidth() / 2, r.GetTop() + r.GetHeight() / 2, 
                r.GetWidth() / 2 );
        }
        else 
        if ( m_shape == Triangle )
        {
            const int pointCount = 3;
            wxPoint points[pointCount];

            points[0] = r.GetBottomLeft();
            points[1] = r.GetBottomRight();
            points[2].x = r.GetLeft() + r.GetWidth() / 2;
            points[2].y = r.GetTop();

            dc.DrawPolygon(pointCount, points);
        }
        else
            wxFAIL_MSG("Invalid LED shape");        
    }    
};


class MyFrame : public wxFrame
{
public:
    MyFrame() : wxFrame(nullptr, wxID_ANY, "Test", wxDefaultPosition, wxSize(640, 640))
    {          
        wxBoxSizer* mainSizer = new wxBoxSizer(wxVERTICAL);

        wxButton* button = new wxButton(this, wxID_ANY, "Load image file for background of the third panel...");
        mainSizer->Add(button, wxSizerFlags().Expand().Border());
        button->Bind(wxEVT_COMMAND_BUTTON_CLICKED, &MyFrame::OnLoadImageFile, this);

        wxPanel* panel1 = new wxPanel(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxFULL_REPAINT_ON_RESIZE);        
        CreateLEDs(panel1);
        mainSizer->Add(panel1, wxSizerFlags().Border());

        wxPanel* panel2 = new wxPanel(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxFULL_REPAINT_ON_RESIZE);
        panel2->SetBackgroundColour(*wxYELLOW);        
        CreateLEDs(panel2);
        mainSizer->Add(panel2, wxSizerFlags().Proportion(1).Expand().Border());

        m_bitmapPanel = new BitmapPanel(this);
        CreateLEDs(m_bitmapPanel);
        mainSizer->Add(m_bitmapPanel, wxSizerFlags().Proportion(1).Expand().Border());
      
        SetSizer(mainSizer);
    }   
protected:       
    BitmapPanel* m_bitmapPanel;

    void CreateLEDs(wxPanel* parent)
    {    
        wxGridSizer* sizer = new wxGridSizer(3);
        
        wxLEDCtrl* LEDCtrl;

        LEDCtrl = new wxLEDCtrl(parent);
        sizer->Add(LEDCtrl, wxSizerFlags().DoubleBorder());

        LEDCtrl = new wxLEDCtrl(parent, wxLEDCtrl::Circle, wxLEDCtrl::On, *wxGREEN, *wxLIGHT_GREY);
        sizer->Add(LEDCtrl, wxSizerFlags().DoubleBorder());

        LEDCtrl = new wxLEDCtrl(parent, wxLEDCtrl::Circle, wxLEDCtrl::Off);
        sizer->Add(LEDCtrl, wxSizerFlags().DoubleBorder());

        LEDCtrl = new wxLEDCtrl(parent, wxLEDCtrl::Triangle, wxLEDCtrl::On, *wxRED, *wxLIGHT_GREY);
        sizer->Add(LEDCtrl, wxSizerFlags().DoubleBorder());

        LEDCtrl = new wxLEDCtrl(parent, wxLEDCtrl::Triangle, wxLEDCtrl::On, *wxGREEN, *wxLIGHT_GREY);
        sizer->Add(LEDCtrl, wxSizerFlags().DoubleBorder());
        
        LEDCtrl = new wxLEDCtrl(parent, wxLEDCtrl::Triangle, wxLEDCtrl::Off);
        sizer->Add(LEDCtrl, wxSizerFlags().DoubleBorder());

        parent->SetSizer(sizer);        
    }
    
    void OnLoadImageFile(wxCommandEvent&)
    {
        static wxString fileName;
        wxImage image;

        fileName = wxFileSelector("Choose Image to Load", "", fileName, "",
            "Image files (*.jpg;*.png;*.tga;*.bmp;*.tiff;*.gif;*.ico;*.pcx)| *.jpg;*.png;*.tga;*.bmp;*.tiff;*.gif;*.ico;*.pcx",
            wxFD_OPEN | wxFD_FILE_MUST_EXIST, this);

        if ( !fileName.empty() && image.LoadFile(fileName) )            
            m_bitmapPanel->SetBitmap(wxBitmap(image));                    
    }
    
};

class MyApp : public wxApp
{
public:   
    bool OnInit() override
    {
        wxInitAllImageHandlers();
        (new MyFrame)->Show();
        return true;
    }
}; wxIMPLEMENT_APP(MyApp);
results on Windows 10 (if you're not on MSW, perhaps SetBackgroundStyle(wxBG_STYLE_TRANSPARENT) could work) in this
leds.png
leds.png (39.38 KiB) Viewed 158 times
Did you get drawing artifacts when resizing etc. with just simple code like above?

BTW, the bitmap panel is just for testing, the image resizing should be done e.g. similarly to viewtopic.php?f=1&t=45099#p186759

EDIT
There were drawing artifacts when resizing a regular panel but that could be fixed by using wxFULL_REPAINT_ON_RESIZE in the panel'style.

Post Reply