Search code examples
c++wxwidgets

wxWidgets - setting an image as background


I am trying to set an image as the background but I have no idea how to do it. It has been hours. The first lines of code I copied from somebody and they seemed alright but it still doesn't work.

The error that I have appears in a separate window and says it cannot load/find the image.

#include "MainFrame.h"
#include <wx/wx.h>
#include "FrameTwo.h"
#include "wx/custombgwin.h"
#include <wx/dcclient.h>

enum IDs {
    BUTTON_ID=2

};

wxBEGIN_EVENT_TABLE(MainFrame, wxFrame)
EVT_BUTTON(BUTTON_ID, MainFrame::OnLoginClicked)
wxEND_EVENT_TABLE()

MainFrame::MainFrame(const wxString& title) : wxFrame(nullptr, wxID_ANY, title) {

    wxPanel* panel = new wxPanel(this);

    wxClientDC dc(panel);
    wxMemoryDC mdc;

    int w, h;
    w = 600;
    h = 600;
    dc.GetSize(&w, &h);

    wxImage img(wxT("C:\\Users\\ALEX\\Desktop\\background_uno.jpeg"), wxBITMAP_TYPE_JPEG);

    wxBitmap cat(img.Scale(w, h, wxIMAGE_QUALITY_HIGH));
    mdc.SelectObject(cat);

    dc.Blit(0, 0, cat.GetWidth(), cat.GetHeight(), &mdc, 0, 0, wxCOPY, 0);

    mdc.SelectObject(wxNullBitmap);

    //panel->SetBackgroundColour(wxColour(255, 102, 102));   //light orange-red
    //panel->SetBackgroundColour(wxColour(155, 202, 62));       //fresh green

    
        wxButton* login = new wxButton(panel, BUTTON_ID, "Log in", wxPoint(340, 350), wxSize(100, 35));
    CreateStatusBar();  //creates a bar in the bottom of the frame
    //wxBoxSizer* sizer = new wxBoxSizer(wxALIGN_CENTER_VERTICAL);
    //sizer -> Add(login, 1, wxEXPAND | wxALL, 10);

    wxButton* exit = new wxButton(panel, wxID_OK, "Exit", wxPoint(340, 390), wxSize(100, 35));

    exit->Bind(wxEVT_BUTTON, &MainFrame::OnExitClicked, this);

    panel->SetFont(panel->GetFont().Scale(2.5));

    wxStaticText* staticText = new wxStaticText(panel, wxID_ANY, "UNO DELUXE", wxPoint(300, 40));

    panel->SetFont(panel->GetFont().Scale(0.5));

    wxStaticText* username = new wxStaticText(panel, wxID_ANY, "Enter username: ", wxPoint(180, 250));

    wxTextCtrl* textCtrl = new wxTextCtrl(panel, wxID_ANY, " ", wxPoint(300, 250), wxSize(250, -1));

    wxStaticText* password = new wxStaticText(panel, wxID_ANY, "Enter password: ", wxPoint(180, 290));

    wxTextCtrl* textCtrl2 = new wxTextCtrl(panel, wxID_ANY, "", wxPoint(300, 290), wxSize(250, -1), wxTE_PASSWORD);
    
}



Solution

  • I can't help you with the program not being able to find the image - the image needs to exist either in the application's working folder or on an absolute path.

    You can get around this by including the image in the executable. Unfortunately there is no consistent cross-platform way to do this - windows and mac have separate ways of doing this but GTK has no way at all. Here are some suggestions from the wiki for some possible workarounds.

    Regardless of the method used to get the image into the application, you should not draw it with a client DC. Instead you should handle the paint event for the panel and draw the image in the handler for that event. Here's an example:

    #include <wx/wx.h>
    
    class ImageBgFrame: public wxFrame
    {
        public:
            ImageBgFrame(wxFrame *frame, const wxString& title);
    
        private:
    
            void OnImagePanelPaint(wxPaintEvent&);
            void OnExit(wxCommandEvent&);
            void CreateScaledBg();
    
            wxPanel* m_imagePanel;
            wxImage m_image;
            wxBitmap m_scaledBg;
    };
    
    ImageBgFrame::ImageBgFrame(wxFrame *frame, const wxString& title)
                 :wxFrame(frame, wxID_ANY, title)
    {
        ::wxInitAllImageHandlers();
    
        // Try to load the image.
        m_image = wxImage("Thinking-of-getting-a-cat.png", wxBITMAP_TYPE_PNG);
        if ( !m_image.IsOk() )
        {
            return;
        }
    
        // Create the controls.
        m_imagePanel = new wxPanel(this, wxID_ANY);
        CreateScaledBg();
        wxButton* exit = new wxButton(m_imagePanel, wxID_OK, "Exit");
    
        // Arrang the controls with a sizer.
        wxBoxSizer* szr = new wxBoxSizer(wxVERTICAL);
        szr->Add(exit,wxSizerFlags().Border(wxALL));
        m_imagePanel->SetSizer(szr);
        Layout();
    
        // Bind Event Handlers
        m_imagePanel->Bind(wxEVT_PAINT, &ImageBgFrame::OnImagePanelPaint, this);
        exit->Bind(wxEVT_BUTTON, &ImageBgFrame::OnExit, this);
    }
    
    void ImageBgFrame::OnImagePanelPaint(wxPaintEvent&)
    {
        if ( m_imagePanel->GetSize() != m_scaledBg.GetSize() )
        {
            CreateScaledBg();
        }
    
        wxPaintDC dc(m_imagePanel);
        dc.DrawBitmap(m_scaledBg,0,0);
    }
    
    void ImageBgFrame::OnExit(wxCommandEvent&)
    {
        Close();
    }
    
    void ImageBgFrame::CreateScaledBg()
    {
        wxSize sz = m_imagePanel->GetSize();
        m_scaledBg = wxBitmap(m_image.Scale(sz.GetWidth(), sz.GetHeight(),
                              wxIMAGE_QUALITY_NORMAL));
    }
    
    
    class ImageBgApp : public wxApp
    {
        public:
            bool OnInit()
            {
                ImageBgFrame* frame = new ImageBgFrame(NULL, "Image BG");
                frame->Show();
                return true;
            }
    };
    
    wxIMPLEMENT_APP(ImageBgApp);
    
    

    This assumes that the image "Thinking-of-getting-a-cat.png" is in the application's working folder. On GTK it looks like this:

    enter image description here

    I've also made a few other changes to the code you gave:

    1. I'm using Bind to connect the event handlers instead of static event tables.
    2. I'm using default sizes for the controls instead of setting the size explicitly and laying out the controls in a sizer instead of setting explicit location.

    Both of these changes are a more modern way of doing things.