创建`wxThread`来为每个`EVT_TREELIST_ITEM_EXPANDED`事件调用后端函数

问题描述 投票:-1回答:2

我有以下课程:

 BEGIN_EVENT_TABLE(MyFrame, wxFrame)
 EVT_TREELIST_ITEM_CHECKED(wxID_ANY, MyFrame::OnItemChecked)
 EVT_TREELIST_ITEM_EXPANDED(wxID_ANY, MyFrame::OnItemExpand)              
 END_EVENT_TABLE()

class MyThread: public wxThread
{
    public:
    MyThread(MyFrame *frame, wxTreeListItem &item);
    virtual void *Entry();
    SeleSyncFrame *m_frame;
    wxTreeListItem item;
};

class MyFrame
{
    friend class MyThread;
    private:
       wxTreeListCtrl* m_treelist;
    public:
       void OnItemExpand(wxTreeListEvent& event);
};

我必须在每个m_treelist事件上更新EVT_TREELIST_ITEM_EXPANDED。为此,我打电话给OnItemExpand()

void MyFrame::OnItemExpand(wxTreeListEvent& event)
{
    wxTreeListItem item = event.GetItem();
    MyThread *thread = new MyThread(this, item);
    if (thread->Create() != wxTHREAD_NO_ERROR)
    {
       dbg.Error(__FUNCTION__, "Can't create thread!");
    }
    thread->Run();
}   

MyThread类的构造函数:

MyThread::MyThread(MyFrame *frame, wxTreeListItem &item) : wxThread()
{
    m_frame = frame;
    this->item = item;
}

MyThread的进入功能:

 wxThread::ExitCode MyThread::Entry()
{
    wxTreeListItem root = m_frame->m_treelist->GetRootItem();
    m_frame->m_treelist->CheckItem(root, wxCHK_CHECKED);

    //This back-end fun is time consuming
        Calltobackend(string resp);
        // I have to convert this string resp into xml and append all items of xml as children for 'item'.
    (m_frame->m_treelist)->AppendItem(item, "child");

    m_frame->m_treelist->CheckItem(item, wxCHK_CHECKED);
    m_frame->m_treelist->UpdateItemParentStateRecursively(m_frame->m_treelist->GetFirstChild(item));
    return NULL;
}

我想为每个浏览请求创建线程,并更新其子项的相应项。我的方法不正确吗?我该怎么做到这一点?我正在考虑另一种方法,我将仅使用线程向后端发送请求,我将使用OnWorkerEvent向主线程发送响应。但是我必须更新由后端返回的响应扩展的项目。那个OnWorkerEvent将如何知道它必须通过响应返回的子项更新树中的哪个项目?

c++ multithreading wxwidgets
2个回答
0
投票

正如VZ所说,从不同的线程更新GUI是一堆蠕虫。不要这样做。

对于你的问题。假设您必须使用来自长任务的值更新控件(在您的情况下,是树形图的项目)。 这个想法很简单:

  • 在您的用户事件处理程序(如OnItemExpand)上,只需创建并运行该线程。不要等待它,让它“脱离”。
  • 在线程代码中,就在它结束之前,由wxQueueEvent()将消息发布到主线程。您需要的值可能是此消息的一部分。或者您也可以使用wxMutex编写一个可访问的var;并使用该消息通知主线程该var已更新。
  • 编写一个新函数(例如MyFrame :: OnMyThreadEnds)而不是处理消息和/或var。您可以在此处更新GUI。

http://docs.wxwidgets.org/trunk/classwx_thread.html


0
投票

您只能使用应用程序的一个(通常是主要的)线程中的GUI对象,因此您的方法根本无法工作。它也不清楚为什么你会为了这样做而创建一个线程的麻烦,这不像在这里的线程中完成任何耗时的操作。

在GUI应用程序中使用线程的标准方法是在后台工作线程中执行任何长时间运行的任务,并将事件发布到主线程以执行GUI更新。你应该像这样构建你的应用程序,除非你有充分的理由不这样做。

更详细地说,传统的方法是让工作线程将wxThreadEvents发布到主线程,其中包含主线程执行操作所需的信息。请注意,wxThreadEventSetPayload()方法,它允许您在线程之间传递任何类型的数据,因此您只需要在worker中调用它,然后在主线程中使用GetPayload()来提取信息并对其进行处理。

但是,因为wxWidgets 3.0你有另一种方法可以使用CallAfter(),如果你使用C ++ 11(你真的应该),这是特别方便的。这允许您在线程函数的范围内编写要执行的代码,但实际上它将在主线程的上下文中执行。所以你可以这样做:

wxThread::ExitCode MyThread::Entry()
{
    wxGetApp().CallAfter([this] {
        wxTreeListItem root = m_frame->m_treelist->GetRootItem();
        m_frame->m_treelist->CheckItem(root, wxCHK_CHECKED);
    });
    ...
}

它实际上会工作,因为lambda中的代码将在主线程中运行。这非常方便,您应该这样做,但只要确保您真正了解这是做什么的,并且它仍然使用相同的基础机制来发布事件来实现其魔力。

© www.soinside.com 2019 - 2024. All rights reserved.