common.json文件中appearance.show_scrollbars
common.json对应于代码的common_settings

1.EDA_DRAW_PANEL_GAL类

EDA_DRAW_PANEL_GAL类中定义了绘图的基本要素:

/// Interface for drawing objects on a 2D-surface
    KIGFX::GAL*              m_gal;

    /// Stores view settings (scale, center, etc.) and items to be drawn
    KIGFX::VIEW*             m_view;

    /// Contains information about how to draw items using GAL
    std::unique_ptr<KIGFX::PAINTER> m_painter;

1.1类图

EDA_DRAW_PANEL_GAL类包含多个子类,其中SCH_DRAW_PANEL是他的子类之一。
KiCad源代码研究:KiCad是如何渲染和绘图的。-LMLPHP

@startuml
class EDA_DRAW_PANEL_GAL {
 KIGFX::GAL*              m_gal;

}
wxScrolledCanvas <|-- EDA_DRAW_PANEL_GAL 
EDA_DRAW_PANEL_GAL *-- GAL
EDA_DRAW_PANEL_GAL <|- SCH_PREVIEW_PANEL
class SCH_DRAW_PANEL{
KIGFX::SCH_VIEW* m_view
KIGFX::WX_VIEW_CONTROLS m_viewControls
}
EDA_DRAW_PANEL_GAL <|-- SCH_DRAW_PANEL
SCH_DRAW_PANEL *-- WX_VIEW_CONTROLS 
SCH_DRAW_PANEL *-- SCH_VIEW
WX_VIEW_CONTROLS *-- SCH_DRAW_PANEL 
WX_VIEW_CONTROLS *-- SCH_VIEW
SCH_VIEW *-- GAL
@enduml

调用SCH_DRAW_PANEL类的地方:

SCH_DRAW_PANEL* SCH_BASE_FRAME::GetCanvas() const
{
    return static_cast<SCH_DRAW_PANEL*>( EDA_DRAW_FRAME::GetCanvas() );
}

SCH_DRAW_PANEL类就是sch_edit_frame位于中央部分的画布,如下图所示,画红框的区域就是SCH_DRAW_PANEL:
KiCad源代码研究:KiCad是如何渲染和绘图的。-LMLPHP
从图中我们可以发现一个问题,如果器件长度过长,超出画布的部分没有办法编辑?正常逻辑出发,我们的发布应当有一个滚动条才对。。
滚动时wxScrolledWindow中的图像损坏

1.2 m_gal

   /// Interface for drawing objects on a 2D-surface
    KIGFX::GAL*              m_gal;

关于GAL类的定义:


namespace KIGFX
{
/**
 * Abstract interface for drawing on a 2D-surface.
 *
 * The functions are optimized for drawing shapes of an EDA-program such as BTD-ABS. Most methods
 * are abstract and need to be implemented by a lower layer, for example by a Cairo or OpenGL
 * implementation.  Almost all methods use world coordinates as arguments. The board design is
 * defined in world space units for drawing purposes these are transformed to screen units with
 * this layer. So zooming is handled here as well.
 *
 */
class GAL : GAL_DISPLAY_OPTIONS_OBSERVER

何时创建m_gal?SwitchBackend中可以创建函数:

bool EDA_DRAW_PANEL_GAL::SwitchBackend( GAL_TYPE aGalType )

2.wxScrolledCanvas类

wxScrolledCanvas类是一个wxWidgets C++库中的类,它提供了一个可滚动的绘图区域,可以在其中绘制图形和文本。它继承了wxScrolledWindow类和wxCanvas类的特性,可以自动处理滚动条和滚动事件,使得用户可以通过滚动条来浏览绘图区域的内容。

2.1wxScrolledCanvas滚动条

可能是因为您的wxScrolledCanvas子类没有设置滚动条的大小和位置。

  • 请确保您已经正确设置了wxScrolledCanvas的虚拟大小(即可滚动的区域大小),并且将这些值传递给wxScrolledCanvas类的SetVirtualSize()方法。
  • 请确保您已经正确设置了wxScrolledCanvas的实际大小(即显示在屏幕上的大小),并且将这些值传递给SetSize方法。

另外,请确保您已经启用了滚动条,可以通过调用wxScrolledCanvas类的SetScrollbars方法来实现。如果问题仍然存在,请检查您的代码是否有其他问题,例如滚动条的位置设置不正确等。
以下是一个简单的wxScrolledCanvas子类的示例代码,它使用了SetScrollbars方法来设置滚动条:

#include <wx/wx.h>
#include <wx/scrolwin.h>

class MyScrolledCanvas : public wxScrolledCanvas
{
public:
    MyScrolledCanvas(wxWindow* parent) : wxScrolledCanvas(parent)
    {
        SetVirtualSize(800, 600); // 设置虚拟大小为800x600
        SetScrollbars(10, 10, 80, 60); // 启用滚动条,每次滚动10个像素,总共有80个像素,滚动区域大小为60个像素
    }

    void OnPaint(wxPaintEvent& event)
    {
        wxPaintDC dc(this);
        dc.SetPen(wxPen(wxColor(255, 0, 0), 2));
        dc.DrawLine(0, 0, 800, 600);
    }

    wxDECLARE_EVENT_TABLE();
};

wxBEGIN_EVENT_TABLE(MyScrolledCanvas, wxScrolledCanvas)
    EVT_PAINT(MyScrolledCanvas::OnPaint)
wxEND_EVENT_TABLE()

class MyFrame : public wxFrame
{
public:
    MyFrame() : wxFrame(nullptr, wxID_ANY, "My Frame")
    {
        auto* canvas = new MyScrolledCanvas(this);
        auto* vbox = new wxBoxSizer(wxVERTICAL);
        vbox->Add(canvas, 1, wxEXPAND);
        SetSizer(vbox);
    }
};

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

wxIMPLEMENT_APP(MyApp);

在上面的示例代码中,我们创建了一个名为MyScrolledCanvas的wxScrolledCanvas子类,并在其构造函数中设置了虚拟大小和滚动条。在OnPaint方法中,我们绘制了一条红色的对角线,以便在滚动时可以看到滚动的效果。最后,在MyFrame类中,我们将MyScrolledCanvas添加到了一个wxBoxSizer中,并将其设置为窗口的唯一子控件。

请注意,这只是一个简单的示例代码,您可以根据自己的需求进行修改和扩展。

关于SetScrollbars方法的参数设置,第一个参数是滚动条每次滚动的单位大小,第二个参数是滚动条的初始位置,第三个参数是滚动条的总长度,第四个参数是可见区域的大小。这些参数的设置应该根据您的实际需求来决定,例如可见区域的大小应该根据窗口大小来设置,而滚动条的单位大小和总长度则应根据您的绘图内容来设置。

对于调用SetVirtualSize方法,它是必须在调用SetScrollbars方法之前完成的。
在默认状态下,要显示滚动条,窗口的虚拟大小必须大于窗口的实际大小。

scr->SetVirtualSize(wxSize(2000,2000));

然而,在3.1版本中,这还不足以让滚动条出现。用户还必须设置滚动速率。(对我来说,这感觉像是一个bug,因为似乎应该有一个有效的默认设置。)

scr->SetScrollRate(1, 1);

3.wxScrolledCanvas滚动条Sample

一个wxScrolledCanvas实现滚动条的sample,来自网络
这是一个最小的框架样本,它有一个分割器,左边有位图,右边有一个面板。

// 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“更改为计算机上实际图像文件的名称(如果图像文件不是png,则更改wxBITMAP_TYPE_PNG )。

重要的部分是:

将画布的背景样式设置为wxBG_STYLE_PAINT,如第46行所示。
在油漆处理程序中,调用油漆the上的DoPrepareDC方法。
顺便说一下,我的样本中显示的油漆处理程序不是很好。它每次都会绘制整个位图。一种更好的方法是获取更新区域,只重新绘制所需的部分,但我想保持这个简单。

3.1实现局部更新:

上文中说,一种更好的方法是获取更新区域,只重新绘制所需的部分应该怎样实现

要实现只重新绘制所需的部分,可以通过以下步骤来获取更新区域并进行相应的绘制:

  • 在窗口的绘图事件处理程序(如OnPaint)中,使用wxPaintDC或wxClientDC对象获取设备上下文(DC)。

  • 调用DC的**GetUpdateRegion()**方法,该方法将返回一个表示需要更新的区域的wxRegion对象。

  • 遍历更新区域,对于每个矩形区域,进行相应的绘制操作。可以使用DC的绘图函数(如DrawRectangle、DrawLine等)或自定义的绘图函数来实现。

这种方法可以减少不必要的绘制操作,只对需要更新的区域进行绘制,从而提高绘图效率。

以下是一个简单的示例代码,演示了如何获取更新区域并进行相应的绘制:

c
Copy
void MyCanvas::OnPaint(wxPaintEvent& event)
{
    wxPaintDC dc(this);
    wxRegionIterator updIterator(dc.GetUpdateRegion());

    while (updIterator)
    {
        wxRect rect = updIterator.GetRect();

        // 在需要更新的区域进行相应的绘制操作
        dc.DrawRectangle(rect);

        ++updIterator;
    }
}

在上述示例代码中,我们使用wxPaintDC对象获取设备上下文,并通过dc.GetUpdateRegion()方法获取更新区域。然后,我们遍历更新区域,并对每个矩形区域进行绘制操作(在这里是绘制矩形)。您可以根据实际需求,在相应的区域内执行您需要的绘制操作。

通过这种方式,您可以只重新绘制需要更新的部分,从而提高绘图效率,并减少不必要的绘制操作。

11-20 19:19