6

I wrote a wxPython program that I am translating to wxWidgets. The program has a scrolled window that displays an image. Following Rappin, wxPython In Action (Listing 12.1), I used a StaticBitmap within a panel. While surfing the latest wxWidgets documentation, I found a dire warning that wxStaticBitmap should only be used for very small images. It says, "... you should use your own control if you want to display larger images portably." Okay. Show me. I don't have my "own control."

Was Rappin wrong, or is the documentation out of date?

The question - a newbie one, no doubt - is what is the right way to do a simple image-view window in wxWidgets? A drop-in replacement for wxStaticBitmap would be nice. I looked into the "image" program in the wxWidgets "samples" directory. It's as long a War and Peace. Surely there must be a canned class or a simple recipe.

Jive Dadson
  • 16,680
  • 9
  • 52
  • 65
  • perhaps rather than 'Invalidate()', mghie meant 'Refresh()'. (This should be a mod to answer 0, but I don't see how to accomplish that.) – contributor Apr 27 '10 at 18:15

2 Answers2

3

Don't let the size of the "image" sample fool you, only a few lines of code are necessary to do what you want.

Search for the MyImageFrame class in the image.cpp file, it is nothing more than a class with a private bitmap field, a custom constructor to set the bitmap and the window client size, and an event handler for EVT_PAINT:

void OnPaint(wxPaintEvent& WXUNUSED(event))
{
    wxPaintDC dc( this );
    dc.DrawBitmap( m_bitmap, 0, 0, true /* use mask */ );
}

Since you don't want a frame class here's your recipe: You create a simple descendant of wxWindow that has a similar constructor, paint handler and duplicates the methods of wxStaticBitmap that you use in your code. Maybe simply one method to set a new bitmap and resize the control to the new bitmap dimensions.

mghie
  • 32,028
  • 6
  • 87
  • 129
  • I'm marking that as the answer. It looks easy. I haven't actually gotten it to work yet, but I just started. :-) – Jive Dadson Feb 18 '10 at 21:30
  • An actual working example would be great. I've figured out that I have to throw in "PrepareDC(dc)" after the "wxPaintDC dc(this);" I have no idea what that is or why it's needed. Just monkey-see monkey-doing. I've got it displaying the images correctly now. But I've yet to be able to capture a mouse-click on the image. Still struggling. Onward through the fog! – Jive Dadson Feb 20 '10 at 02:27
  • @Jive: See http://docs.wxwidgets.org/2.8/wx_wxscrolledwindow.html#wxscrolledwindowpreparedc, however this is outside of writing a simple `wxStaticBitmap` replacement. For mouse clicks see http://docs.wxwidgets.org/2.8/wx_wxmouseevent.html#wxmouseevent, your class should be able to handle those, so you only need to connect a handler. *view.cpp* in the *docvwmdi* sample has a canvas class (descendant of `wxScrolledWindow`) that does both drawing and mouse handling, maybe studying this will help. – mghie Feb 20 '10 at 09:59
  • 1
    I've got that part working now. In fact, I already did. I thought I had a breakpoint set in the mouse-handler, but I didn't. Duh. It's too easy to ask questions on Stackoverflow! I've been doing this stuff full time almost continuously since 1971, but I manage to come off like a dolt. Oh well. On to the next alligator. I think it's deciding to garbage-collect my cursors, and I don't know why. – Jive Dadson Feb 20 '10 at 16:41
2
// A scrolled window for showing an image.
class PictureFrame: public wxScrolledWindow
{   
public:
    PictureFrame()
        : wxScrolledWindow()
        , bitmap(0,0)
    {;}

    void Create(wxWindow *parent, wxWindowID id = -1)
    {
        wxScrolledWindow::Create(parent, id);
    }

    void LoadImage(wxImage &image) {
        bitmap = wxBitmap(image);
        SetVirtualSize(bitmap.GetWidth(), bitmap.GetHeight());
        wxClientDC dc(this);
        PrepareDC(dc);
        dc.DrawBitmap(bitmap, 0, 0);
    }

protected:
    wxBitmap bitmap;

    void OnMouse(wxMouseEvent &event) {
        int xx,yy;
        CalcUnscrolledPosition(event.GetX(), event.GetY(), &xx, &yy);
        event.m_x = xx; event.m_y = yy;
        event.ResumePropagation(1); // Pass along mouse events (e.g. to parent)
        event.Skip();
    }

    void OnPaint(wxPaintEvent &event) {
        wxPaintDC dc(this);
        PrepareDC(dc);
        dc.DrawBitmap(bitmap, 0,0, true);
    }
private:
    DECLARE_EVENT_TABLE()
};

BEGIN_EVENT_TABLE(PictureFrame,wxScrolledWindow)
    EVT_PAINT(PictureFrame::OnPaint)
    EVT_MOUSE_EVENTS(PictureFrame::OnMouse)
END_EVENT_TABLE()
Jive Dadson
  • 16,680
  • 9
  • 52
  • 65
  • You should probably not call any painting code in `LoadImage()`, calling `Invalidate()` should be enough, the control will then be redrawn when it's convenient for the system. It may not be necessary or it may even be impossible to paint at the time `LoadImage()` is called. – mghie Feb 21 '10 at 11:39
  • 1>------ Build started: Project: Munsell_picker, Configuration: Debug Win32 ------ 1>Compiling... 1>main.cpp 1>.\main.cpp(114) : error C3861: 'Invalidate': identifier not found – Jive Dadson Feb 21 '10 at 16:53