博客
关于我
wxWidgets源码分析(6) - 窗口关闭过程
阅读量:434 次
发布时间:2019-03-06

本文共 11386 字,大约阅读时间需要 37 分钟。

目录

窗口关闭过程

调用流程

用户点击窗口的关闭按钮后,Win32系统会向当前的Frame对象发送WM_CLOSE消息,此时会进入到Frame的wxFrame::MSWWindowProc函数进行处理:

WXLRESULT wxFrame::MSWWindowProc(WXUINT message, WXWPARAM wParam, WXLPARAM lParam){    switch ( message )    {        case WM_CLOSE:            // if we can't close, tell the system that we processed the            // message - otherwise it would close us            processed = !Close();            break;...

Close方法是由wxWindowBase类提供的,调用过程如下:

  1. 创建一个wxEVT_CLOSE_WINDOW消息,传递当前的windowID
  2. 调用当前对象的消息处理函数进行处理。
bool wxWindowBase::Close(bool force){    wxCloseEvent event(wxEVT_CLOSE_WINDOW, m_windowId);    event.SetEventObject(this);    event.SetCanVeto(!force);    // return false if window wasn't closed because the application vetoed the    // close event    return HandleWindowEvent(event) && !event.GetVeto();}

继续跟踪wxEVT_CLOSE_WINDOW消息的处理,前面我们有介绍单文档的Frame窗口的继承关系如下,我们可以根据此继承关系找消息处理函数:

wxDocParentFrame -> wxDocParentFrameBase (wxDocParentFrameAny < wxFrame > ) --    wxFrame & wxDocParentFrameAnyBase

我们可以找到wxDocParentFrameAny模板类中的Create方法中注册了wxEVT_CLOSE_WINDOW消息的处理函数是wxDocParentFrameAny<>::OnCloseWindow

// wxDocParentFrameAny < >bool Create(wxDocManager *manager, ...){	m_docManager = manager;	if ( !BaseFrame::Create(frame, id, title, pos, size, style, name) )		return false;	this->Connect(wxID_EXIT, wxEVT_MENU,				  wxCommandEventHandler(wxDocParentFrameAny::OnExit));	this->Connect(wxEVT_CLOSE_WINDOW,				  wxCloseEventHandler(wxDocParentFrameAny::OnCloseWindow));	return true;}

wxDocParentFrameAny<>::OnCloseWindow会调用m_docManager的Clear方法来实现关闭,只要是MVC类,则必然存在文档管理类。

// wxDocParentFrameAny < >void OnCloseWindow(wxCloseEvent& event){	if ( m_docManager && !m_docManager->Clear(!event.CanVeto()) )	{		// The user decided not to close finally, abort.		event.Veto();	}	else	{		// Just skip the event, base class handler will destroy the window.		event.Skip();	}}

接着我们看下文档管理类的wxDocManager::Clear方法实现:

  1. 调用CloseDocuments关闭所有文档,这步很关键,关闭文档的关键路径;
  2. 由于wxDocManager管理多个文档模板,此时需要将所有的文档模板对象全部删除。
bool wxDocManager::Clear(bool force){    if (!CloseDocuments(force))        return false;    m_currentView = NULL;    wxList::compatibility_iterator node = m_templates.GetFirst();    while (node)    {        wxDocTemplate *templ = (wxDocTemplate*) node->GetData();        wxList::compatibility_iterator next = node->GetNext();        delete templ;        node = next;    }    return true;}

跟踪关键路径wxDocManager::CloseDocuments,这里的操作步骤就是找到此文档管理器管理的所有文档,然后调用CloseDocument方法关闭文档:

bool wxDocManager::CloseDocuments(bool force){    wxList::compatibility_iterator node = m_docs.GetFirst();    while (node)    {        wxDocument *doc = (wxDocument *)node->GetData();        wxList::compatibility_iterator next = node->GetNext();        if (!CloseDocument(doc, force))            return false;        node = next;    }    return true;}

执行关闭文档操作,几个步骤:

  1. 调用doc类的Close方法执行关闭;
  2. 调用doc类的DeleteAllViews方法关闭此文档关联的View;
  3. delete文档对象
bool wxDocManager::CloseDocument(wxDocument* doc, bool force){    if ( !doc->Close() && !force )        return false;    doc->DeleteAllViews();    if (m_docs.Member(doc))        delete doc;    return true;}

关闭文档

wxDocManager调用文档类的wxDocument::Close方法执行关闭,这个方法的默认实现是关闭此文档类关联的所有子文档对象,然后调用用户可以实现的OnCloseDocument方法。

OnCloseDocument的默认实现仅仅是进行清理而已。

bool wxDocument::Close(){    if ( !OnSaveModified() )        return false;    DocsList::const_iterator it = m_childDocuments.begin();    for ( DocsList::const_iterator end = m_childDocuments.end(); it != end; ++it )    {        if ( !(*it)->OnSaveModified() )        {            return false;        }    }    while ( !m_childDocuments.empty() )    {        wxDocument * const childDoc = m_childDocuments.front();        if ( !childDoc->Close() )        {            wxFAIL_MSG( "Closing the child document unexpectedly failed "                        "after its OnSaveModified() returned true" );        }        childDoc->DeleteAllViews();    }    return OnCloseDocument();}bool wxDocument::OnCloseDocument(){    // Tell all views that we're about to close    NotifyClosing();    DeleteContents();    Modify(false);    return true;}

删除视图

调用wxDocument::DeleteAllViews删除此文档关联的所有视图,对于一个View来说有两个操作步骤:

  1. 调用wxView::Close方法关闭view;
  2. 调用delete删除view对象。
  3. 当存在多个View时,逐个调用删除
bool wxDocument::DeleteAllViews(){    wxDocManager* manager = GetDocumentManager();    // first check if all views agree to be closed    const wxList::iterator end = m_documentViews.end();    for ( wxList::iterator i = m_documentViews.begin(); i != end; ++i )    {        wxView *view = (wxView *)*i;        if ( !view->Close() )            return false;    }    // all views agreed to close, now do close them    if ( m_documentViews.empty() )    {        if ( manager && manager->GetDocuments().Member(this) )            delete this;    }    else // have views    {        for ( ;; )        {            wxView *view = (wxView *)*m_documentViews.begin();            bool isLastOne = m_documentViews.size() == 1;            delete view;            if ( isLastOne )                break;        }    }    return true;}

View类的Close方法比较简单,就是调用用户的OnClose方法,当然OnClose也有默认实现,wxView::OnClose的默认实现就是调用wxDocument::Close,前文已经描述过,不赘述:

bool wxView::Close(bool deleteWindow){    return OnClose(deleteWindow);}bool wxView::OnClose(bool WXUNUSED(deleteWindow)){    return GetDocument() ? GetDocument()->Close() : true;}

我们还需关注delete view;这行语句,这行实现了view与doc类的脱离关系,如下,最终调用m_viewDocument->RemoveView实现了将自己从doc中移除:

wxView::~wxView(){    if (m_viewDocument && GetDocumentManager())        GetDocumentManager()->ActivateView(this, false);    if ( m_docChildFrame && m_docChildFrame->GetView() == this )    {        m_docChildFrame->SetView(NULL);        m_docChildFrame->GetWindow()->Destroy();    }    if ( m_viewDocument )        m_viewDocument->RemoveView(this);}

删除文档对象

跟踪wxDocument::RemoveView,可以看到此时会从doc的m_documentViews列表中删除此view,然后再调用OnChangedViewList进行后处理,OnChangedViewList检查到当前doc不关联任何view的时候,会发起自己的删除delete this

bool wxDocument::RemoveView(wxView *view){    (void)m_documentViews.DeleteObject(view);    OnChangedViewList();    return true;}void wxDocument::OnChangedViewList(){    if ( m_documentViews.empty() && OnSaveModified() )        delete this;}

关闭Frame

在前文中我们看到Doc对象的关闭是动态注册的处理函数,由于最后在wxDocParentFrameAny<>::OnCloseWindow方法中调用event.Skip(),这样这个消息还会继续传递处理,接下来继续查找静态消息表,我们可以看到在wxTopLevelWindowBase中有如下定义:

BEGIN_EVENT_TABLE(wxTopLevelWindowBase, wxWindow)    EVT_CLOSE(wxTopLevelWindowBase::OnCloseWindow)    EVT_SIZE(wxTopLevelWindowBase::OnSize)END_EVENT_TABLE()

那么此时会继续静态消息处理表的处理,对应的处理入口消息为wxTopLevelWindowBase::OnCloseWindow

  1. 将自己加入到wxPendingDelete队列中,这样下次IDLE的时候会一并删除;
  2. 隐藏待删除的窗口;
void wxTopLevelWindowBase::OnCloseWindow(wxCloseEvent& WXUNUSED(event)){    Destroy();}bool wxTopLevelWindowBase::Destroy(){    if ( wxWindow* parent = GetParent() )    {        if ( parent->IsBeingDeleted() )            return wxNonOwnedWindow::Destroy();    }    if ( !wxPendingDelete.Member(this) )        wxPendingDelete.Append(this);    for ( wxWindowList::const_iterator i = wxTopLevelWindows.begin(),                                     end = wxTopLevelWindows.end();          i != end;          ++i )    {        wxTopLevelWindow * const win = static_cast
(*i); if ( win != this && win->IsShown() ) { Hide(); break; } } return true;}

App清理

再回过头来,我们看看APP类的清理,APP类的启动过程可以参考启动代码部分,这里回到调用点wxEntryReal,程序执行完成后会从wxTheApp->OnRun()中返回,也就是退出wxTRY的作用域,我们来重点看下CallOnExit的作用:

在定义CallOnExit类的时候一起创建了一个对象callOnExit,此对象位于栈上,那么退出wxTRY的作用域时必然会调用到此对象的析构函数,参考定义,我们看到析构函数最终调用的是wxTheApp->OnExit()

这样通过临时对象的析构函数调用了APP类的OnExitfangf

int wxEntryReal(int& argc, wxChar **argv){    wxInitializer initializer(argc, argv);    if ( !initializer.IsOk() )    {        return -1;    }    wxTRY    {        if ( !wxTheApp->CallOnInit() )        {            return -1;        }        // ensure that OnExit() is called if OnInit() had succeeded        class CallOnExit        {        public:            ~CallOnExit() { wxTheApp->OnExit(); }        } callOnExit;        WX_SUPPRESS_UNUSED_WARN(callOnExit);        return wxTheApp->OnRun();    }    wxCATCH_ALL( wxTheApp->OnUnhandledException(); return -1; )}

多文档窗口的关闭

多文档父窗口关闭

多文档窗口的父窗口用于容纳一个或者多个子窗口,关闭过程同wxFrame的正常关闭过程。

如何通知到子窗口?注意看Destroy。。。

多文档子窗口关闭

一般使用中,我们会独立关闭多文档APP的子窗口,操作过程是用户点击了子窗口Frame的关闭按钮,消息的产生过程与单文档窗口类似,我们可以找到wxDocChildFrameAny<>类中Create函数中创建了对wxEVT_CLOSE_WINDOW消息的动态绑定wxDocChildFrameAny::OnCloseWindow

bool Create(wxDocument *doc, ...){	if ( !wxDocChildFrameAnyBase::Create(doc, view, this) )		return false;	if ( !BaseClass::Create(parent, id, title, pos, size, style, name) )		return false;	this->Connect(wxEVT_ACTIVATE,				  wxActivateEventHandler(wxDocChildFrameAny::OnActivate));	this->Connect(wxEVT_CLOSE_WINDOW,				  wxCloseEventHandler(wxDocChildFrameAny::OnCloseWindow));	return true;}

OnCloseWindow方法的调用比较简单,我们逐个看实现:

// wxDocChildFrameAny < > void OnCloseWindow(wxCloseEvent& event){	if ( CloseView(event) )		Destroy();	//else: vetoed}

wxDocChildFrameAnyBase::CloseView用于关闭view,会调用View的Close方法,前文我们知道View在关闭的时候会再次调用doc的Close方法,这样就可以保证视图和文档对象都关闭了。

关闭完成后执行删除视图就可以了,在删除视图的析构函数中会再次调用文档的RemoveView,当文档对象检查到自己没有关联的视图对象时,文档会删除自己。

到此,视图和文档就已经删除了。

bool wxDocChildFrameAnyBase::CloseView(wxCloseEvent& event){    if ( m_childView )    {        if ( !m_childView->Close(false) && event.CanVeto() )        {            event.Veto();            return false;        }        m_childView->Activate(false);        m_childView->SetDocChildFrame(NULL);        wxDELETE(m_childView);    }    m_childDocument = NULL;    return true;}

继续我们看下wxDocChildFrameAny<>::Destroy方法,这个方法会调用父类实现的Destroy,调用Destroy方法后就会销毁窗口,同普通窗口销毁。

// wxDocChildFrameAny < > virtual bool Destroy(){	// FIXME: why exactly do we do this? to avoid activation events during	//        destructions maybe?	m_childView = NULL;	return BaseClass::Destroy();}

我们再关注下子窗口是何时与父窗口脱离关系的呢?找到wxMDIChildFrame的析构函数:

  1. 调用GetMDIParent()->RemoveMDIChild方法恢复父类原来的菜单;
  2. 调用MSWDestroyWindow将自己与父类窗口脱离关系
wxMDIChildFrame::~wxMDIChildFrame(){    // if we hadn't been created, there is nothing to destroy    if ( !m_hWnd )        return;    GetMDIParent()->RemoveMDIChild(this);    m_frameToolBar = NULL;    m_frameStatusBar = NULL;    DestroyChildren();    MDIRemoveWindowMenu(NULL, m_hMenu);    MSWDestroyWindow();}

这样子窗口删除后,父窗口会继续存活。

窗口的正式删除

App处于空闲状态时会调用wxAppConsoleBase::ProcessIdle方法,这个方法进而调用DeletePendingObjects用于清理待删除的窗口对象。

操作过程就是从wxPendingDelete中取得所有的待删除的对象,delete掉。

bool wxAppConsoleBase::ProcessIdle(){    // synthesize an idle event and check if more of them are needed    wxIdleEvent event;    event.SetEventObject(this);    ProcessEvent(event);// Garbage collect all objects previously scheduled for destruction.    DeletePendingObjects();    return event.MoreRequested();}void wxAppConsoleBase::DeletePendingObjects(){    wxList::compatibility_iterator node = wxPendingDelete.GetFirst();    while (node)    {        wxObject *obj = node->GetData();        if ( wxPendingDelete.Member(obj) )            wxPendingDelete.Erase(node);        delete obj;        node = wxPendingDelete.GetFirst();    }}

窗口关闭过程总结

下面对关闭时用户可以捕捉的时机做个总结,用户可以通过下面的方法来截获此消息。

类型 用户重载的方法
wxApp virtual int OnExit ()
wxDocument virtual bool OnCloseDocument ()
wxView virtual bool OnClose (bool deleteWindow)
wxFrame virtual bool Destroy () // 重载时必须要调用父类的方法

如何手工删除view

从上面的分析,我们看到删除view之前,我们需要先调用wxView::Close方法,此时会调用用户的

OnClose方法,然后再delete掉。

void deleteView(wxView *view){	view->Close();	delete view}

转载地址:http://toyfz.baihongyu.com/

你可能感兴趣的文章
QBlog V2.5 源码开放下载(ASP.NET 番外系列之开端)
查看>>
稀疏数组
查看>>
Android MediaPlayer setDataSource failed
查看>>
虚拟机搭建hadoop环境
查看>>
Hibernate入门(四)---------一级缓存
查看>>
[Python学习笔记]组织文件
查看>>
Spring Boot 2.x基础教程:构建RESTful API与单元测试
查看>>
dojo/request模块整体架构解析
查看>>
互联网App应用程序测试流程及测试总结
查看>>
IntelliJ IDEA 中,项目文件右键菜单没有svn选项解决办法
查看>>
微软XAML Studio - WPF, Sliverlight, Xamarin, UWP等技术开发者的福音
查看>>
(在模仿中精进数据可视化07)星球研究所大坝分布可视化
查看>>
(数据科学学习手札27)sklearn数据集分割方法汇总
查看>>
阿里巴巴Json工具-Fastjson教程
查看>>
Spring security OAuth2.0认证授权学习第二天(基础概念-RBAC)
查看>>
PySide图形界面开发(一)
查看>>
882. Reachable Nodes In Subdivided Graph
查看>>
375. Guess Number Higher or Lower II
查看>>
41. First Missing Positive
查看>>
80. Remove Duplicates from Sorted Array II
查看>>