滚动时 wxScrolledWindow 中的图像损坏

Image corruption in wxScrolledWindow while scrolling

我想使用 wxScrolledWindow 在其中一个 wxSplitterWindow 中查看图像。 我从 wxPanel 派生了 class,我将其添加到 wxScrolledWindow 中。但是结果不是我想要的。(

class ImagePanel : public wxPanel
{
public:
    //ImagePanel(wxFrame* parent);
    ImagePanel(wxWindow* parent);
    void paintEvent(wxPaintEvent& event);
    void paintNow();

    void setBitmap(wxBitmap& imBitmap);
    void setBitmap(wxImage& img);

    int GetHeight();
    int GetWidth();

    void render(wxDC& dc);

    DECLARE_EVENT_TABLE()
private:
    wxBitmap curBitmap;
};

绘画事件:

void ImagePanel::paintEvent(wxPaintEvent& event)
{
    wxBufferedPaintDC dc(this);
    render(dc);
}

这是我将接口写入主框架构造函数的方式:

topSizer = new wxBoxSizer(wxVERTICAL);

    topSplitter = new wxSplitterWindow(this,ID_TOPSPLITTER,wxDefaultPosition,wxDefaultSize,wxSP_LIVE_UPDATE|wxSP_3D);
    topSizer->Add(topSplitter,1,wxALL|wxEXPAND,0);

    imagePanel = new wxPanel(topSplitter,ID_IMAGEPANEL);
    imageSizer = new wxBoxSizer(wxVERTICAL);
    imagePanel->SetSizer(imageSizer);
    optionPanel = new wxPanel(topSplitter,ID_OPTIONPANEL);
    optionSizer = new wxBoxSizer(wxVERTICAL);
    optionPanel->SetSizer(optionSizer);
    topSplitter->SplitVertically(imagePanel, optionPanel);
    topSplitter->SetMinimumPaneSize(200);

    //===========================================
    //image panel interface
    imageScWindow=new wxScrolledWindow(imagePanel,ID_IMAGESCWINDOW,wxDefaultPosition,wxDefaultSize,
                                       wxVSCROLL|wxHSCROLL|wxALL|wxALWAYS_SHOW_SB|wxFULL_REPAINT_ON_RESIZE);
    imgArea = new ImagePanel(imageScWindow);
    imageSizer->Add(imageScWindow,1,wxEXPAND,0);

    scrollSizer = new wxBoxSizer(wxVERTICAL);
    imageScWindow->SetSizer(scrollSizer);
    scrollSizer->Add(imgArea,1,wxEXPAND,0);
    imageScWindow->SetTargetWindow(imgArea);

这是加载图片的函数:

void MainFrame::Setpic(wxCommandEvent& event)

{
    wxString path = GetCurrentWorkingDir()+wxT("\")+tmptxt1->GetValue();
    wxImage img(path);
    wxBitmap imBitmap(img,-1);
    imgArea->setBitmap(imBitmap);
    imageScWindow->SetScrollbars(1,1,imgArea->GetWidth(),imgArea->GetHeight());
    imageScWindow->Refresh();
}

这就是我得到的: https://i.imgur.com/x1GIlrl.png

我也试过在构造函数中这样做,没有 SetTargetArea:

//===========================================
    //image panel interface
    imageScWindow=new wxScrolledWindow(imagePanel,ID_IMAGESCWINDOW,wxDefaultPosition,wxDefaultSize,
                                       wxVSCROLL|wxHSCROLL|wxALL|wxALWAYS_SHOW_SB|wxFULL_REPAINT_ON_RESIZE);
    imgArea = new ImagePanel(imageScWindow);
    imageSizer->Add(imageScWindow,1,wxEXPAND,0);

    scrollSizer = new wxBoxSizer(wxVERTICAL);
    imageScWindow->SetSizer(scrollSizer);
    scrollSizer->Add(imgArea,1,wxEXPAND,0);

然后图像可以正常显示,但是如果我更改框架大小或移动拆分器,滚动条就会消失,我必须在某处重新设置它们。 然后我试着这样做:

int prvx,prvy;
void ImagePanel::paintEvent(wxPaintEvent& event)
{
    reinterpret_cast<wxScrolledWindow*>(this->GetParent())->GetViewStart(&prvx,&prvy);
    reinterpret_cast<wxScrolledWindow*>(this->GetParent())->SetScrollbars(1,1,curBitmap.GetWidth(),curBitmap.GetHeight());
    reinterpret_cast<wxScrolledWindow*>(this->GetParent())->Scroll(wxPoint(prvx,prvy));
    wxBufferedPaintDC dc(this);
    render(dc);
}

它显示正确,但在滚动时滞后,即使在 wxScrolledWindow 中没有 wxFULL_REPAINT_ON_RESIZE。它在 paint 事件中的原因:如果我将它添加到 OnSize 事件中,程序会崩溃。怎么办?

我不知道从哪里开始回答你的问题。这是一个最小的帧示例,它有一个拆分器,左侧是位图,右侧是面板。我希望你能调整这个示例来做你需要的。

// 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/scrolwin.h>
#include <wx/splitter.h>
#include <wx/dcbuffer.h>

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 );
    private:
        void OnScrollPaint( wxPaintEvent& event );

        wxScrolledCanvas* m_canvas;
        wxBitmap m_bitmap;
};

MyFrame::MyFrame( wxWindow* parent, int id, wxString title, wxPoint pos,
                  wxSize size, int style )
        :wxFrame( parent, id, title, pos, size, style )
{
    m_bitmap=wxBitmap ("test.png", wxBITMAP_TYPE_PNG   );

    wxSplitterWindow* m_splitter1
        = new wxSplitterWindow( this, wxID_ANY, wxDefaultPosition,
                                wxDefaultSize, wxSP_LIVE_UPDATE );

    m_canvas = new wxScrolledCanvas( m_splitter1, wxID_ANY, wxDefaultPosition,
         wxDefaultSize, wxSTATIC_BORDER|wxHSCROLL|wxVSCROLL );

    m_canvas->SetScrollRate( 5, 5 );
    m_canvas->SetVirtualSize(m_bitmap.GetWidth(), m_bitmap.GetHeight());
    m_canvas->SetBackgroundStyle(wxBG_STYLE_PAINT);
    m_canvas->Bind( wxEVT_PAINT, &MyFrame::OnScrollPaint , this );

    wxPanel* m_panel2 = new wxPanel( m_splitter1, wxID_ANY, wxDefaultPosition,
        wxDefaultSize, wxSTATIC_BORDER|wxTAB_TRAVERSAL );

    m_splitter1->SplitVertically( m_canvas, m_panel2, GetSize().x/2 );
}

void MyFrame::OnScrollPaint( wxPaintEvent& event )
{
    wxAutoBufferedPaintDC dc(m_canvas);
    m_canvas->DoPrepareDC(dc);
    dc.Clear();
    dc.DrawBitmap(m_bitmap,0,0);
}

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

wxIMPLEMENT_APP(MyApp);

为了 运行 这个,一定要将第 35 行的 "test.png" 更改为您计算机上实际图像文件的名称(如果图像文件不是',则更改 wxBITMAP_TYPE_PNG t png).

重要的部分是:

  1. 将 canvas 的背景样式设置为 wxBG_STYLE_PAINT,如第 46 行所示。
  2. 在绘制处理程序中,调用 paintdc 上的 DoPrepareDC 方法。

顺便说一句,我的示例中显示的油漆处理程序不是很好。它每次都绘制整个位图。更好的方法是获取更新区域并只重绘需要的部分,但我想保持简单。