wxWidgets wxThreadHelper 示例问题

wxWidgets wxThreadHelper Example Question

我对 wxWidgets 比较陌生,正试图掌握如何正确实现线程和事件以及所有这些好东西。我能够在我的项目目录中得到一个简单的框架 运行 就好了,但是当我 运行 在尝试添加线程时遇到问题。

我 运行 回购的“示例”中给出的 Thread 演示没有问题,但据我了解,最近的首选方法是使用 wxThreadHelper,因为您不需要传递框架指针。

我基本上是从给出的 wxThreadHelper 示例开始的 here。我真正做的就是把东西分成 header 和 src.

Header:

// For compilers that support precompilation, includes "wx/wx.h".
#include "wx/wxprec.h"

// 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

#if !wxUSE_THREADS
    #error "This sample requires thread support!"
#endif // wxUSE_THREADS


wxDECLARE_EVENT(EVT_COMMS_UPDATE, wxThreadEvent);

enum {LAYOUT_TEST_PROPORTIONS = wxID_HIGHEST+1,
        LAYOUT_TEST_SIZER,
        LAYOUT_TEST_NB_SIZER
    };


class wxMainFrame : public wxFrame, public wxThreadHelper, private wxLog {
private:

public:
    wxMainFrame();
    ~wxMainFrame(){
        // it's better to do any thread cleanup in the OnClose()
        // event handler, rather than in the destructor.
        // This is because the event loop for a top-level window is not
        // active anymore when its destructor is called and if the thread
        // sends events when ending, they won't be processed unless
        // you ended the thread from OnClose.
        // See @ref overview_windowdeletion for more info.
    }
    void onCommsUpdate_t();
    void OnClose(wxCloseEvent& evt);
    
    wxTextCtrl * m_txtctrl;
    
    
protected:
    virtual wxThread::ExitCode Entry();
    
    //fields update from threads
    double data_t;
    wxCriticalSection m_dataCS; // protects field above - i don't really know how this thing scopes the CS
    
    wxDECLARE_EVENT_TABLE();

};

源代码:

#include "wxMainFrame.h"

wxDEFINE_EVENT(EVT_COMMS_UPDATE, wxThreadEvent)
wxBEGIN_EVENT_TABLE(wxMainFrame, wxFrame)
    EVT_THREAD(EVT_COMMS_UPDATE, wxMainFrame::onCommsUpdate_t)
    EVT_CLOSE(wxMainFrame::OnClose)
wxEND_EVENT_TABLE()

wxMainFrame::wxMainFrame() : wxFrame(NULL, wxID_ANY, "wxWidgets Layout Demo") {
    
    //Enable thread
    if (CreateThread(wxTHREAD_JOINABLE) != wxTHREAD_NO_ERROR){
        wxLogError("Could not create the worker thread!");
        return;
    }
    // go!
    if (GetThread()->Run() != wxTHREAD_NO_ERROR) {
        wxLogError("Could not run the worker thread!");
        return;
    }
    
    this->data_t = 0;
    
    // Make a menubar
    wxMenu *file_menu = new wxMenu;

    file_menu->Append(LAYOUT_TEST_PROPORTIONS, "&Proportions demo...\tF1");
    file_menu->Append(LAYOUT_TEST_SIZER, "Test wx&FlexSizer...\tF2");
    file_menu->Append(LAYOUT_TEST_NB_SIZER, "Test &notebook sizers...\tF3");
    wxMenuBar *menu_bar = new wxMenuBar;

    menu_bar->Append(file_menu, "&File");

    // Associate the menu bar with the frame
    SetMenuBar(menu_bar);
    
    

    
    // create the logging text control and a header showing the meaning of the
    // different columns
    wxTextCtrl *header = new wxTextCtrl(this, wxID_ANY, "",
                                        wxDefaultPosition, wxDefaultSize,
                                        wxTE_READONLY);
    //DoLogLine(header, "  Time", " Thread", "Message");
    m_txtctrl = new wxTextCtrl(this, wxID_ANY, "",
                               wxDefaultPosition, wxDefaultSize,
                               wxTE_MULTILINE | wxTE_READONLY); 
    
    wxLog::SetActiveTarget(this);

    // use fixed width font to align output in nice columns
    wxFont font(wxFontInfo().Family(wxFONTFAMILY_TELETYPE));
    header      ->SetFont(font);
    m_txtctrl   ->SetFont(font);

    m_txtctrl->SetFocus();  //Field to have focus on startu

    // layout and show the frame
    wxBoxSizer *sizer = new wxBoxSizer(wxVERTICAL);
    sizer->Add(header, wxSizerFlags().Expand());
    sizer->Add(m_txtctrl, wxSizerFlags(1).Expand());    
    SetSizer(sizer);

    SetSize(600, 350);
    
}

wxThread::ExitCode wxMainFrame::Entry(){
    // IMPORTANT: this function gets executed in the secondary thread context!
    while (!GetThread()->TestDestroy()) {
        // since this Entry() is implemented in MyFrame context we don't
        // need any pointer to access the m_data, m_processedData, m_dataCS
        // variables... very nice!
        wxCriticalSectionLocker lock(m_dataCS);
        
        this->data_t++;
        
        // VERY IMPORTANT: do not call any GUI function inside this
        //                 function; rather use wxQueueEvent():
        wxQueueEvent(this, new wxThreadEvent(EVT_COMMS_UPDATE));
    }
    
    // TestDestroy() returned true (which means the main thread asked us
    // to terminate as soon as possible) or we ended the long task...
    return (wxThread::ExitCode)0;
}

void wxMainFrame::onCommsUpdate_t(){
    //Do thread stuff
    //wxCriticalSectionLocker lock(m_dataCS);
    //std::cout << this->data_t;
}

void wxMainFrame::OnClose(wxCloseEvent& evt){
    // important: before terminating, we _must_ wait for our joinable
    // thread to end, if it's running; in fact it uses variables of this
    // instance and posts events to *this event handler
    if (GetThread() &&      // DoStartALongTask() may have not been called
        GetThread()->IsRunning())
        GetThread()->Wait();
    Destroy();
}

导致此编译错误:

In file included from /usr/local/include/wx-3.1/wx/wx.h:24,
                 from includes/wxMainFrame.h:10,
                 from src/wxMainFrame.cpp:2:
/usr/local/include/wx-3.1/wx/event.h:4350:5: error: expected ‘,’ or ‘;’ before ‘                                                            const’
 4350 |     const wxEventTable theClass::sm_eventTable = \
      |     ^~~~~
src/wxMainFrame.cpp:5:1: note: in expansion of macro ‘wxBEGIN_EVENT_TABLE’
    5 | wxBEGIN_EVENT_TABLE(wxMainFrame, wxFrame)

如您所见,这是一个基本语法错误,如果我进入 /usr/local/include/wx-3.1/wx/event.h:4350

,我可以 see/fix

但这没有意义,我总不能是第一个发现简单语法错误的人吧?我的机器一定有问题 (ubuntu)。这是从 make install 添加到我的系统中的 header,对吗?如果从源代码构建的 demos/Samples 一切正常,怎么会出现语法错误。我想只是大声思考。

我使用 here 的建议从源代码构建了库。

我也在 wxWidget 的论坛 here 上看到了这个 post。谁似乎没有遇到与我相同的语法问题。而且,我真的不明白如何使用建议的宏,因为 wiki 说这个宏实际上是由 wx__DECLARE_EVT2 包装的。如果这是解决方案,也许一个简单的示例将有助于向我展示如何正确使用它。

感谢任何意见。谢谢!

PS 我已经尝试 post 在论坛上回答这个问题,但无法注册(尝试使用多个浏览器)所以我来了。

这里有很多问题。首先,你提到的编译错误是由于缺少分号,行:

wxDEFINE_EVENT(EVT_COMMS_UPDATE, wxThreadEvent)

应该是

wxDEFINE_EVENT(EVT_COMMS_UPDATE, wxThreadEvent);

其次,onCommsUpdate_t方法需要带一个wxThreadEvent&参数

void onCommsUpdate_t(wxThreadEvent&);

显然,方法的定义也需要更新。


第三,老实说,我不知道 EVT_THREAD 宏应该如何工作,但我知道行

EVT_THREAD(EVT_COMMS_UPDATE, wxMainFrame::onCommsUpdate_t)

不起作用。处理线程事件的最简单方法是使用 Bind 方法。在框架构造函数中添加这样一行来处理事件

Bind(EVT_COMMS_UPDATE,&wxMainFrame::onCommsUpdate_t,this);

第四,正如所写,您的 wxMainFrame::Entry 方法将不断向事件队列发送垃圾邮件,从而冻结应用程序。在真实的应用程序中,大概线程会在排队每个事件之前做一些工作。因为在这个例子中,没有工作正在做,你应该添加一行

wxMilliSleep(500);

到 while 循环的末尾,让线程在发送每个事件之前休眠一会儿。


第五,您实际上从未向线程发出关闭信号,它也永远不会自行关闭。因此,行 GetThread()->Wait(); 将是一个无限循环。有许多方法可以向线程发送信号,但最简单的方法可能是在框架 class 中添加一个名为 m_shutdown 的 bool 成员。那么

一个。在框架构造函数中将 m_shutdown 初始化为 false。

b。在wxMainFrame::OnClose中,调用Wait前使用临界区设置为真:

if (GetThread() &&      // DoStartALongTask() may have not been called
    GetThread()->IsRunning())
{
    {
        wxCriticalSectionLocker lock(m_dataCS);
        m_shutdown = true;
    }
    
    GetThread()->Wait();
}

c。在wxMainFrame::Entry方法中,在while循环(由临界区保护)中添加对m_shutdown值的检查。

    while (!GetThread()->TestDestroy()) {
        // since this Entry() is implemented in MyFrame context we don't
        // need any pointer to access the m_data, m_processedData, m_dataCS
        // variables... very nice!
        {
            wxCriticalSectionLocker lock(m_dataCS);
            this->data_t++;
            if ( m_shutdown )
                break;
        }
...

六、不知道是什么行

wxLog::SetActiveTarget(this);

应该这样做,但它会在程序退出时导致崩溃。如果需要,您需要存储初始日志目标并在帧析构函数或关闭事件处理程序中恢复它。