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

时间:2017-12-19 19:34:50

标签: c++ multithreading wxwidgets

我有以下课程:

 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将如何知道它必须通过响应返回的子项更新树中的哪个项目?

2 个答案:

答案 0 :(得分:0)

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

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

更详细地说,传统的方法是让工作线程将wxThreadEvent发布到主线程,其中包含主线程执行操作所需的信息。请注意,wxThreadEvent具有SetPayload()方法,允许您在线程之间传递任何类型的数据,因此您只需要在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中的代码将在主线程中运行。这非常方便,您应该这样做,但只要确保您真正了解这是做什么的,并且它仍然使用相同的基础机制来发布事件以实现其魔力。

答案 1 :(得分:0)

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

针对您的问题。让我们假设您必须使用来自长任务的值更新控件(在您的情况下,是一个树状列表的项目)。
这个想法很简单:

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

请参阅http://docs.wxwidgets.org/trunk/classwx_thread.html