【wxWidgets实战】窗口自适应滚动

本文详细介绍了如何使用wxWidgets库实现窗口自适应滚动功能。通过创建`MyApp`类作为程序入口,继承自`wxApp`,创建主窗口`MainWindow`继承自`wxFrame`,并在主窗口内添加面板`MainPanel`继承自`wxPanel`。通过布局管理器调整滚动窗口`ScrollWin`的布局,并利用定时器动态添加或移除静态文本控件,以展示窗口自适应滚动效果。
摘要由CSDN通过智能技术生成

先上效果图——
效果图

一、项目构建思路

1. 创建程序入口:“MyApp”类

  • 继承 wxApp 类,创建 MyApp 类,并重写 OnInit() 函数。
  • OnInit() 函数中创建程序主窗口 MyFrame

2. 创建主窗口

  • 继承 wxFrame 创建 MyFrame 类。

3. 给主窗口添加面板

  • 继承 wxPanel 创建 MyPanel 类。
  • 在主窗口中创建一个面板(MyPanel),该面板作为主窗口的唯一控件(如此一来这个面板将会默认占满整个主窗口的客户区域)。

4. 给面板添加内容

  • 给面板添加一个布局。
  • 在面板中添加滚动窗口 wxScrolledWindow,并且把滚动窗口放到布局中。
  • 使用定时器定时创建或销毁静态文本控件(wxStaticText)。

二、代码分解(省略大部分注释)

1. 入口

程序入口类 MyApp。它继承自 wxApp,并且重写了 OnInit() 方法。在 OnInit() 中,创建了一个 MyFrame 对象,将其显示出来,最后返回true表示程序初始化成功。

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

wxIMPLEMENT_APP(MyApp);

2. 创建主窗口

主窗口类 MyFrame。它继承自 wxFrame,在其构造函数中,调用了基类 wxFrame 的构造函数来创建一个窗口,并在这个窗口中新建了一个 MyPanel 面板。

class MyFrame : public wxFrame
{
public:
    MyFrame()
      : wxFrame(NULL, wxID_ANY, "Scrollable Panel Example", wxDefaultPosition, wxSize(400, 300))
    {
        new MyPanel(this);
    }
};

3. 给主窗口添加面板

首先,看看如何创建面板并添加到主窗口中。

创建了名为 MyPanel 的面板类,它继承自 wxPanelMyPanel 类在构造函数中,首先创建了一个名为 sizer 的垂直布局器(wxBoxSizer)。

wxBoxSizer *sizer = new wxBoxSizer(wxVERTICAL);

然后创建了一个名为 m_scrolledWindow 的滚动窗口(wxScrolledWindow),并将其添加到 sizer 中。这样滚动窗口就能占据面板的全部空间。

m_scrolledWindow = new wxScrolledWindow(this);
sizer->Add(m_scrolledWindow, 1, wxEXPAND | wxALL, 5);

接着,创建了一个名为 m_innerSizer 的垂直布局器,并将其设置为滚动窗口的布局器,以及设置滚动窗口的滚动速率。

m_innerSizer = new wxBoxSizer(wxVERTICAL);
m_scrolledWindow->SetSizer(m_innerSizer);
m_scrolledWindow->SetScrollRate(0, 20);

最后,将 sizer 设置为面板的布局器。

SetSizer(sizer);

4. 给面板添加内容

在这部分代码中,设置了一个定时器用于定时添加和移除控件,并绑定了定时器事件的处理函数。

m_count = 0;
m_timer = new wxTimer(this, wxID_ANY);
Bind(wxEVT_TIMER, &MyPanel::OnTimer, this);
m_timer->Start(500);

定义了一个函数对象 funRefreshScrollWindow,用于刷新滚动窗口。在这个函数中,首先获取 m_innerSizer 的最小高度并设置为滚动窗口的虚拟高度,然后根据滚动窗口的高度和面板的高度来判断是否需要启用滚动条,如果需要则将滚动窗口滚动到最底部,最后刷新滚动窗口的显示。

auto funRefreshScrollWindow = [this](bool increase) {
	// 调整滚动窗口的滚动范围(重点)
	int totalHeight = m_innerSizer->GetMinSize().y;
	m_scrolledWindow->SetVirtualSize(-1, totalHeight);

	// 检查是否需要启用滚动条
	wxSize panelSize = GetClientSize();
	if (totalHeight > panelSize.y)
		m_scrolledWindow->EnableScrolling(increase, increase);

	// 手动滚动到最底部
	int scrollPos = totalHeight - panelSize.y;
	m_scrolledWindow->Scroll(0, scrollPos);

	// 刷新滚动窗口
	m_scrolledWindow->Refresh();
};

定时器事件的处理函数为 OnTimer。在这个函数中,根据 m_count 的值来增加或移除 wxStaticText 控件,每当增加或移除一个控件时就调用 funRefreshScrollWindow 来刷新滚动窗口的显示。当 m_count 超过 20 时,停止定时器。

if (m_count <= 10) {
	wxString text = wxString::Format("Item %d", m_count);
	wxStaticText *item = new wxStaticText(m_scrolledWindow, wxID_ANY, text);
	m_innerSizer->Add(item, 0, wxALL, 5);

	funRefreshScrollWindow(true);
}
else if (m_count <= 20) {
	wxWindow *lastItem = m_innerSizer->GetItem(m_innerSizer->GetItemCount() - 1)->GetWindow();
	m_innerSizer->Detach(lastItem);
	lastItem->Destroy();

	funRefreshScrollWindow(false);
}
else {
	m_timer->Stop();
}

m_count++;

三、重点代码分析

  1. wxBoxSizer *sizer = new wxBoxSizer(wxVERTICAL);:创建一个垂直方向的盒型布局器。布局器是 wxWidgets 中的一种工具,用于管理控件的位置和大小。
  2. sizer->Add(m_scrolledWindow, 1, wxEXPAND | wxALL, 5);:将滚动窗口 m_scrolledWindow 添加到布局器 sizer 中。wxEXPAND | wxALL 表示滚动窗口应扩展以填充其在布局器中的空间,并在所有方向上都应添加边距,边距的大小为5个像素。
  3. m_scrolledWindow->SetSizer(m_innerSizer);:将内部的布局器 m_innerSizer 设置为滚动窗口 m_scrolledWindow 的布局器。这将使滚动窗口内的控件按照 m_innerSizer 的布局来排列。
  4. m_timer = new wxTimer(this, wxID_ANY); 和 m_timer->Start(500);:创建并启动一个定时器,定时器每 500毫秒 触发一次。定时器用于定时添加和删除静态文本控件。
  5. Bind(wxEVT_TIMER, &MyPanel::OnTimer, this);:将定时器事件绑定到 MyPanel::OnTimer 函数。这意味着每次定时器触发时,都会调用 MyPanel::OnTimer 函数。
  6. int totalHeight = m_innerSizer->GetMinSize().y;:获取内部布局器 m_innerSizer 的最小高度。这个高度值代表了内部布局器(及其内部的所有控件)的最小合适高度。
  7. m_scrolledWindow->SetVirtualSize(-1, totalHeight);:设置滚动窗口 m_scrolledWindow 的虚拟大小。虚拟大小是指滚动窗口内部的大小,可能大于滚动窗口本身的显示大小。如果虚拟大小大于显示大小,那么滚动窗口就会显示出滚动条。
  8. wxStaticText *item = new wxStaticText(m_scrolledWindow, wxID_ANY, text);:创建一个静态文本控件,并将其添加到滚动窗口中。静态文本控件用于显示一些不可编辑的文本信息。
  9. m_timer->Stop();:停止定时器。这个调用会停止定时器的触发,之后定时器不再触发,除非再次调用 Start() 方法启动定时器。

四、完整代码(带注释)

#include <wx/wx.h>

class MyPanel : public wxPanel
{
public:
    MyPanel(wxWindow *parent)
      : wxPanel(parent)
    {
        wxBoxSizer *sizer = new wxBoxSizer(wxVERTICAL);

        // 创建滚动窗口并添加到面板中
        m_scrolledWindow = new wxScrolledWindow(this);
        sizer->Add(m_scrolledWindow, 1, wxEXPAND | wxALL, 5);

        m_innerSizer = new wxBoxSizer(wxVERTICAL);
        m_scrolledWindow->SetSizer(m_innerSizer);

        SetSizer(sizer);

        // 设置滚动速率
        m_scrolledWindow->SetScrollRate(0, 20);

        // 初始化计数器和定时器
        m_count = 0;
        m_timer = new wxTimer(this, wxID_ANY);
        Bind(wxEVT_TIMER, &MyPanel::OnTimer, this);

        // 启动定时器,每500毫秒添加一个控件
        m_timer->Start(500);
    }

private:
    void OnTimer(wxTimerEvent &event)
    {
        // 刷新滚动窗口
        auto funRefreshScrollWindow = [this](bool increase) {
            // 调整滚动窗口的滚动范围(重点)
            int totalHeight = m_innerSizer->GetMinSize().y;
            m_scrolledWindow->SetVirtualSize(-1, totalHeight);

            // 检查是否需要启用滚动条
            wxSize panelSize = GetClientSize();
            if (totalHeight > panelSize.y)
                m_scrolledWindow->EnableScrolling(increase, increase);

            // 手动滚动到最底部
            int scrollPos = totalHeight - panelSize.y;
            m_scrolledWindow->Scroll(0, scrollPos);

            // 刷新滚动窗口
            m_scrolledWindow->Refresh();
        };

        if (m_count <= 10) {  // 每次定时器触发时,增加一个控件,并刷新滚动窗口
            wxString text = wxString::Format("Item %d", m_count);
            wxStaticText *item = new wxStaticText(m_scrolledWindow, wxID_ANY, text);
            m_innerSizer->Add(item, 0, wxALL, 5);

            funRefreshScrollWindow(true);
        }
        else if (m_count <= 20) {  // 每次定时器触发时,逐渐移除一个控件,并刷新滚动窗口
            wxWindow *lastItem = m_innerSizer->GetItem(m_innerSizer->GetItemCount() - 1)->GetWindow();
            m_innerSizer->Detach(lastItem);
            lastItem->Destroy();

            funRefreshScrollWindow(false);
        }
        else {
            // 停用定时器
            m_timer->Stop();
        }

        m_count++;
    }

private:
    wxScrolledWindow *m_scrolledWindow;
    wxBoxSizer *m_innerSizer;
    int m_count;
    wxTimer *m_timer;
};

class MyFrame : public wxFrame
{
public:
    MyFrame()
      : wxFrame(NULL, wxID_ANY, "Scrollable Panel Example", wxDefaultPosition, wxSize(400, 300))
    {
        new MyPanel(this);
    }
};

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

wxIMPLEMENT_APP(MyApp);

五、要点分析

  1. 面板和滚动窗口的布局管理:面板(wxPanel)和滚动窗口(wxScrolledWindow)都使用了 wxBoxSizer 进行布局管理。在 wxWidgets 中,布局管理是一个重要的概念,它决定了窗口中各个控件的位置和大小。理解和熟悉各种布局器(wxSizer 的子类)的工作方式是很重要的。
  2. 虚拟大小和滚动条:在滚动窗口(wxScrolledWindow)中,需要设置虚拟大小(SetVirtualSize)来启用滚动条。虚拟大小是滚动窗口内部的大小,如果这个大小大于滚动窗口本身的显示大小,滚动窗口就会显示出滚动条。那有没有什么办法可以始终禁用滚动条呢?有,只需向这样调用函数——m_scrolledWindow->ShowScrollbars(wxSHOW_SB_NEVER, wxSHOW_SB_NEVER),你要的效果就可以实现了。
  3. 定时器的使用:定时器(wxTimer)在这个程序中用来定时添加和删除控件。使用定时器时,需要注意的是要绑定定时器事件(wxEVT_TIMER)到相应的处理函数,以便在定时器触发时能执行相应的操作。
  4. wxWidgets事件处理系统:在wxWidgets中,事件处理是通过绑定事件到处理函数来实现的。在这个程序中,定时器事件(wxEVT_TIMER)被绑定到了 MyPanel::OnTimer 函数(正如第3点所言)。理解 wxWidgets 的事件处理系统是掌握 wxWidgets 的关键。
  5. 程序的入口:在 wxWidgets 程序中,通常需要定义一个继承自 wxApp 的类,并在其 OnInit 方法中创建主窗口。然后使用 wxIMPLEMENT_APP 宏来指定这个类作为程序的入口。在理解这个程序时,需要先找到这个程序的入口,也就是 wxApp 的子类。

【wxWidgets实战】窗口自适应滚动 至此完毕,欢迎大家指正!在此祝愿大家工作顺利,日日高升~~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Xiao_Ley

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值