// Modified by:
// Created: 06/16/98
// RCS-ID: $Id$
-// Copyright: (c) 1998-2002 wxWidgets team
+// Copyright: (c) 1998-2009 wxWidgets team
// Licence: wxWindows license
/////////////////////////////////////////////////////////////////////////////
// the main application frame
// ----------------------------------------------------------------------------
-class MyFrame: public wxFrame
+class MyFrame : public wxFrame,
+ private wxLog
{
public:
// ctor
- MyFrame(wxFrame *frame, const wxString& title, int x, int y, int w, int h);
+ MyFrame(const wxString& title);
virtual ~MyFrame();
- // this function is MT-safe, i.e. it can be called from worker threads
- // safely without any additional locking
- void LogThreadMessage(const wxString& text)
- {
- wxCriticalSectionLocker lock(m_csMessages);
- m_messages.push_back(text);
-
- // as we effectively log the messages from the idle event handler,
- // ensure it's going to be called now that we have some messages to log
- wxWakeUpIdle();
- }
-
// accessors for MyWorkerThread (called in its context!)
bool Cancelled();
+protected:
+ virtual void DoLogRecord(wxLogLevel level,
+ const wxString& msg,
+ const wxLogRecordInfo& info);
+
private:
// event handlers
// --------------
void OnUpdateWorker(wxUpdateUIEvent& event);
+ // logging helper
+ void DoLogLine(wxTextCtrl *text,
+ const wxString& timestr,
+ const wxString& threadstr,
+ const wxString& msg);
+
+
// thread helper functions
// -----------------------
// update display in our status bar: called during idle handling
void UpdateThreadStatus();
- // log the messages queued by LogThreadMessage()
- void DoLogThreadMessages();
-
// internal variables
// ------------------
// just some place to put our messages in
wxTextCtrl *m_txtctrl;
+ // old log target, we replace it with one using m_txtctrl during this
+ // frame life time
+ wxLog *m_oldLogger;
+
// the array of pending messages to be displayed and the critical section
// protecting it
wxArrayString m_messages;
class MyThread : public wxThread
{
public:
- MyThread(MyFrame *frame);
+ MyThread();
virtual ~MyThread();
// thread execution starts here
virtual void *Entry();
- // write something to the text control in the main frame
- void WriteText(const wxString& text)
- {
- m_frame->LogThreadMessage(text);
- }
-
public:
unsigned m_count;
- MyFrame *m_frame;
};
// ----------------------------------------------------------------------------
wxLog::AddTraceMask("thread");
// Create the main frame window
- MyFrame *frame = new MyFrame((wxFrame *)NULL, _T("wxWidgets threads sample"),
- 50, 50, 450, 340);
- SetTopWindow(frame);
-
- // Show the frame
- frame->Show(true);
+ new MyFrame("wxWidgets threads sample");
return true;
}
END_EVENT_TABLE()
// My frame constructor
-MyFrame::MyFrame(wxFrame *frame, const wxString& title,
- int x, int y, int w, int h)
- : wxFrame(frame, wxID_ANY, title, wxPoint(x, y), wxSize(w, h))
+MyFrame::MyFrame(const wxString& title)
+ : wxFrame(NULL, wxID_ANY, title)
{
+ m_oldLogger = wxLog::GetActiveTarget();
+
SetIcon(wxIcon(sample_xpm));
// Make a menubar
m_nRunning = m_nCount = 0;
- m_dlgProgress = (wxProgressDialog *)NULL;
+ m_dlgProgress = NULL;
#if wxUSE_STATUSBAR
CreateStatusBar(2);
#endif // wxUSE_STATUSBAR
- m_txtctrl = new wxTextCtrl(this, wxID_ANY, _T(""), wxPoint(0, 0), wxSize(0, 0),
+ // 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(wxNORMAL_FONT->GetPointSize(), wxFONTFAMILY_TELETYPE,
+ wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL);
+ header->SetFont(font);
+ m_txtctrl->SetFont(font);
+
+ m_txtctrl->SetFocus();
+
+ // 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);
+ Show();
}
MyFrame::~MyFrame()
{
+ wxLog::SetActiveTarget(m_oldLogger);
+
// NB: although the OS will terminate all the threads anyhow when the main
// one exits, it's good practice to do it ourselves -- even if it's not
// completely trivial in this example
wxGetApp().m_semAllDone.Wait();
}
-MyThread *MyFrame::CreateThread()
+void
+MyFrame::DoLogLine(wxTextCtrl *text,
+ const wxString& timestr,
+ const wxString& threadstr,
+ const wxString& msg)
{
- MyThread *thread = new MyThread(this);
+ text->AppendText(wxString::Format("%9s %10s %s", timestr, threadstr, msg));
+}
- if ( thread->Create() != wxTHREAD_NO_ERROR )
+void
+MyFrame::DoLogRecord(wxLogLevel level,
+ const wxString& msg,
+ const wxLogRecordInfo& info)
+{
+ // let the default GUI logger treat warnings and errors as they should be
+ // more noticeable than just another line in the log window and also trace
+ // messages as there may be too many of them
+ if ( level <= wxLOG_Warning || level == wxLOG_Trace )
{
- wxLogError(wxT("Can't create thread!"));
+ m_oldLogger->LogRecord(level, msg, info);
+ return;
}
- wxCriticalSectionLocker enter(wxGetApp().m_critsect);
- wxGetApp().m_threads.Add(thread);
-
- return thread;
+ DoLogLine
+ (
+ m_txtctrl,
+ wxDateTime(info.timestamp).FormatISOTime(),
+ info.threadId == wxThread::GetMainId()
+ ? wxString("main")
+ : wxString::Format("%x", info.threadId),
+ msg + "\n"
+ );
}
-void MyFrame::DoLogThreadMessages()
+MyThread *MyFrame::CreateThread()
{
- wxCriticalSectionLocker lock(m_csMessages);
+ MyThread *thread = new MyThread;
- const size_t count = m_messages.size();
- for ( size_t n = 0; n < count; n++ )
+ if ( thread->Create() != wxTHREAD_NO_ERROR )
{
- m_txtctrl->AppendText(m_messages[n]);
+ wxLogError(wxT("Can't create thread!"));
}
- m_messages.clear();
+ wxCriticalSectionLocker enter(wxGetApp().m_critsect);
+ wxGetApp().m_threads.Add(thread);
+
+ return thread;
}
void MyFrame::UpdateThreadStatus()
void MyFrame::OnIdle(wxIdleEvent& event)
{
- DoLogThreadMessages();
-
UpdateThreadStatus();
event.Skip();
wxMessageDialog dialog(this,
_T("wxWidgets multithreaded application sample\n")
_T("(c) 1998 Julian Smart, Guilhem Lavaux\n")
- _T("(c) 1999 Vadim Zeitlin\n")
- _T("(c) 2000 Robert Roebling"),
+ _T("(c) 2000 Robert Roebling\n")
+ _T("(c) 1999,2009 Vadim Zeitlin"),
_T("About wxThread sample"),
wxOK | wxICON_INFORMATION);
void MyFrame::OnStartGUIThread(wxCommandEvent& WXUNUSED(event))
{
+ // we use this to check that disabling logging only affects the main thread
+ // but the messages from the worker thread will still be logged
+ wxLogNull noLog;
+ wxLogMessage("You shouldn't see this message because of wxLogNull");
+
MyImageDialog dlg(this);
dlg.ShowModal();
// paint a sort of progress bar with a 10px border:
dc.SetBrush(*wxRED_BRUSH);
- dc.DrawRectangle(10,10, 10+m_nCurrentProgress*(GUITHREAD_BMP_SIZE-20)/100,30);
+ dc.DrawRectangle(10,10, m_nCurrentProgress*(sz.GetWidth()-20)/100,30);
dc.SetTextForeground(*wxBLUE);
dc.DrawText(wxString::Format("%d%%", m_nCurrentProgress),
(sz.GetWidth()-dc.GetCharWidth()*2)/2,
// MyThread
// ----------------------------------------------------------------------------
-MyThread::MyThread(MyFrame *frame)
+MyThread::MyThread()
: wxThread()
{
m_count = 0;
- m_frame = frame;
}
MyThread::~MyThread()
wxThread::ExitCode MyThread::Entry()
{
- wxString text;
-
- text.Printf(wxT("Thread %p started (priority = %u).\n"),
- GetId(), GetPriority());
- WriteText(text);
- // wxLogMessage(text); -- test wxLog thread safeness
+ wxLogMessage("Thread started (priority = %u).", GetPriority());
for ( m_count = 0; m_count < 10; m_count++ )
{
if ( TestDestroy() )
break;
- text.Printf(wxT("[%u] Thread %p here.\n"), m_count, GetId());
- WriteText(text);
+ wxLogMessage("Thread progress: %u", m_count);
// wxSleep() can't be called from non-GUI thread!
wxThread::Sleep(1000);
}
- text.Printf(wxT("Thread %p finished.\n"), GetId());
- WriteText(text);
- // wxLogMessage(text); -- test wxLog thread safeness
+ wxLogMessage("Thread finished.");
return NULL;
}
wxThread::ExitCode MyGUIThread::Entry()
{
+ // uncomment this to check that disabling logging here does disable it for
+ // this thread -- but not the main one if you also comment out wxLogNull
+ // line in MyFrame::OnStartGUIThread()
+ //wxLogNull noLog;
+
+ // this goes to the main window
+ wxLogMessage("GUI thread starting");
+
+ // use a thread-specific log target for this thread to show that its
+ // messages don't appear in the main window while it runs
+ wxLogBuffer logBuf;
+ wxLog::SetThreadActiveTarget(&logBuf);
+
for (int i=0; i<GUITHREAD_NUM_UPDATES && !TestDestroy(); i++)
{
// inform the GUI toolkit that we're going to use GUI functions
// notify the dialog that another piece of our masterpiece is complete:
wxThreadEvent event( wxEVT_COMMAND_THREAD, GUITHREAD_EVENT );
- event.SetInt(i);
+ event.SetInt(i+1);
wxQueueEvent( m_dlg, event.Clone() );
+ if ( !((i + 1) % 10) )
+ {
+ // this message will go to the buffer
+ wxLogMessage("Step #%d.", i + 1);
+ }
+
// give the main thread the time to refresh before we lock the GUI mutex again
// FIXME: find a better way to do this!
wxMilliSleep(100);
}
+ // now remove the thread-specific thread target
+ wxLog::SetThreadActiveTarget(NULL);
+
+ // so that this goes to the main window again
+ wxLogMessage("GUI thread finished.");
+
return (ExitCode)0;
}