15.4 错误报告
有时候你需要在控制台或者一个对话框中显示一条消息以帮助调试或者用来提示那些不能被你的代码正常处理的行为.wxWidgets 提供了很多用于记录错误的函数,这些函数工作方式各不相同,你可以使用它们来进行运行情况的报告和记录.比如,当你正在分配一个很大的图片时,可能由于这个图片太大了,系统无法分配足够的资源,系统将使用 wxLogError 函数显示一个对话框来报告这个错误(如下图所示).又或者说,你想将某个参数的值打印在调试窗口中以便于调试,你可以使用 wxLogDebug 函数.究竟这些错误信息或者调试信息是显示在终端上,对话框中还是别的什么地方,取决于你所使用的函数,以及当前激活的 wxLog 目标对象,我们将在稍后的部分描述相关内容。
所有的这种记录函数都拥有类似于 printf 或 vprintf 的语法,也就是:第一个参数是格式化文本参数,后面是不定类型的变量或者一组指向变量的指针,如下所示:
wxString name(wxT("Calculation"));
int nGoes = 3;
wxLogError(wxT("%s does not compute! You have %d more goes."),
name.c_str(), nGoes);
下面我们逐个描述一下这些函数:
wxLogError 函数用来显示那些必须显示给用户的错误消息.其默认行为是弹出一个对话框来通知用户相应的错误.那为什么不直接使用 wxMessageBox 呢?原因是,首先 wxLogError 提示的错误消息是可以通过创建一个 wxLogNull 的 Log 目标来屏蔽让其不显示出来的,而且这些消息也是排在系统队列中,在系统空闲的时候显示的.因此如果有一系列的错误同时出现,它们将显示在同一个对话框中,而如果使用 wxMessageBox,你的用户可能不得不不停的点击 OK 按钮。
wxLogFatalError 和 wxLogError 类似,不过除了显示错误消息,它还使用标准的系统调用 abort,以错误码 3 结束整个程序的运行.和其它类似的函数不同,这个函数显示的消息不能通过设置空的打印目标的方法来屏蔽。
wxLogWarning 也和 wxLogError 类似,不过显示的信息将作为警告而不是错误。
wxLogMessage 则用来显示所有正常的,信息类型的消息,默认也是显示在对话框中。
wxLogVerbose 则用来显示那些冗长的详细信息.通常情况下,这种信息是不显示的,但是如果用户想显示它以便了解程序运行的更详细的情况,可以通过使用 wxLog::SetVerbose 函数改变这种默认的行为。
wxLogStatus 则用来显示状态条消息,如果当前的 frame 窗口拥有一个状态条,那么这个消息将显示在那里。
wxLogSysError 通常主要被 wxWidgets 自己使用,它用来报告那些系统错误,同时会显示由 errno 或者 GetLastError(依平台的不同) 指示的错误码和错误消息.它的另外一种形式允许你的第一个参数的位置显式的指示系统错误码。
wxLogDebug 用来显式调试信息.这些信息只在调试版本中(定义了 WXDEBUG 宏) 才会出现,在正式版本中将被移除.在 windows 平台上,只有当程序在一个调试器中运行或者使用第三方工具比如来自 http://www.sysinternals.com 的 DebugView 工具运行的时候才会显示出来。
wxLogTrace 和 wxLogDebug 的功能几乎完全一样,也是只在调试模式才会显示信息.之所以有这个函数,是为了提供一个和普通的调试模式不同的级别以便区分普通的调试信息和用于跟踪的调试信息.它的另外一种形式允许你指定一个掩码,通过 wxLog:: AddTraceMask 函数设置了掩码以后,只有掩码符合的跟踪消息才会被显示出来以实现跟踪消息的过滤.比如在 wxWidgets 内部使用了 mousecapture 掩码.如果你设置了这个掩码,在鼠标移动的时候你将看到跟踪信息。
void wxWindowBase::CaptureMouse()
{
wxLogTrace(wxT("mousecapture"), wxT("CaptureMouse(%p) "), this);
...
}
void MyApp::OnInit()
{
// Add mousecapture to the list of trace masks
wxLog::AddTraceMask(wxT("mousecapture"));
...
}
你可能会疑惑,为什么不直接使用 C 的标准输入输出函数或者 C++的流呢?简短的回答是,它们都是很不错的机制,但是并不一定适用于 wxWidgets.wxLog 机制主要有以下三个优点:
首先,wxLog 是可移植的.常用的 printf 语句或者 C++的 cout 流和 cerr 流在 unix 系统下工作是没有问题的,但是在 windows 系统中,对于图形化界面的应用程序,这些函数或流可能不能正常显示需要的内容.因此,你可以使用 wxLogMessage 作为 printf 的一个简单的替代品。
你也可以通过下面的方法将所有的 log 信息转向标准的 cout 流:
wxLog *logger=new wxLogStream(&cout);
wxLog::SetActiveTarget(logger);
另外,将发送往 cout 的输出重定向到一个 wxTextCtrl 控件也是可行的,这需要使用到 wxStreamToTextRedirector 类。
其次:wxLog 更灵活.使用 wxLog 机制的输出可以被分情况重定向或者隐藏,比如只显示错误消息和告警消息,忽略所有正常的信息.而如果使用标准的函数或流,这是不可能的或者说是很难作到的。
最后:wxLog 机制也是更完善的机制.通常,当有错误发生的时候,应该给用户显示一些信息.让我们来举一个简单的例子,假如你正在进行写文件操作,这时候发生了磁盘空间不足的情况,这种错误是被 wxWidgets 内部(wxFile::Write) 处理的,因此,调用这个函数只能知道写动作发生了异常,至于是什么类型的异常则很难得到,如果在这种情况下使用 wxLogError 函数,正确的错误码和相应的错误信息都将显示给用户。
现在我们来描述以下 wxWidgets 的 Log 机制是怎样工作的,以便你处理那些默认没有提供的行为。
wxWidgets 有一个 log 目标的概念:它其实就是一个 wxLog 的派生类.它需要实现 wxLog 定义的那些虚函数,这些函数将在相应的 Log 函数被调用的时候使用.任何时候都只有一个 log 目标是活动的.log 目标通常的使用方法就是调用 wxLog:: SetActiveTarget 来安装这个目标,安装以后的目标将在相应的 log 函数被调用的时候自动使用。
要创建一个自定义的 log 目标,你只需要创建一个 wxLog 的派生类,并实现其虚函数 DoLogString 和(或)DoLog.如果你对 wxWidgets 默认的增加时间戳和信息类型的格式化方法感到满意,只是想更改信息的目的地,那么实现 DoLogString 函数就足够了,而重载 DoLog 函数则使得你可以任意的定制输出信息的格式,不过同时你也需要自己区分信息的各种类型.你可以参考 src/common/log.cpp 文件看看 wxWidgets 是怎么作到这一点的。
wxWidgets 自己实现了几个 wxLog 的派生类,你也可以读一读它们的代码,这对你创建自定义的 log 目标也是有好处的.这些预定义的 log 目标包括:
wxLogStderr 将所有的信息输出到 FILE作为参数的文件中,如果 FILE为空,则输出到标准错误输出。
wxLogStream 和 wxLogStderr 功能相同,不过它使用标准 C++的 ostream 类和 cerr 流来代替 FILE*和 stderr。
wxLogGui 则是 wxWidgets 所有 wxWidgets 程序默认使用的 log 目标,依平台的不同它实现了不同的输出处理。
wxLogWindow 则提供了一个类似"跟踪终端"之类的窗口,这个窗口将显示所有的输出信息,同时这些信息也将显示在之前的 log 目标上.这个跟踪终端窗口提供了清除信息,关闭窗口以及将所有信息保存到文件中的功能。
wxLogNull 则被用来临时阻止某些错误信息的输出,比如你打开不存在的文件的时候将显示一个错误信息,有时候你不希望显示这个信息,可以在栈上创建一个 wxLogNull 变量,在这个变量的作用域范围内,没有任何错误信息将被显示,而离开了其作用域,则所有的信息又可以正常显示了。
wxFile file;
// wxFile.Open() 在打开一个不存在的文件时通常会显示错误信息,但是在这里我们不想看到这个信息.
{
wxLogNull logNo;
if ( !file.Open("bar") )
... process error ourselves ...
} // wxLogNull 的析构函数被调用,旧的 log 目标被恢复.
wxLogMessage("..."); // 可以被显示
有时候你也许希望将信息输出到多个地方,比如,你可以希望所有的信息在正常显示的同时被保存在某个文件中,这时候你可以使用 wxLogChain 和 wxLogPassThrough,如下所示:
// 这将隐式的设置当前 log 目标
wxLogChain *logChain = new wxLogChain(new wxLogStderr);
// 所有的输出将被同时显示在 stderr 和通常的地方
// 不要直接删除 logChain 指针,这会导致当前活动 log 目标为一个不确定的指针.
// 应该使用 SetActiveTarget.
delete wxLog::SetActiveTarget(new wxLogGui);
wxMessageOutput VS wxLog
有时候,使用 wxLog 不太合适,这主要是因为 wxLog 对输出的信息作了过多的处理,并且会等待空闲的时候才会显示这些信息.而 wxMessageOutput 和它的派生类则可以作为你的底层 printf 的替代品,来在 GUI 和命令行程序中使用.你可以象使用 printf 函数那样使用 wxMessageOutput::Printf 函数,比如,如果你想把信息打印在标准错误输入:
#include "wx/msgout.h"
wxMessageOutputStderr err;
err.Printf(wxT("Error in app %s.\n"), appName.c_str());
wxMessageOutputDebug 将信息显示在调试器终端或者是标准错误输出中,这主要看程序是以什么方式运行的,和 wxLogDebug 不同,wxMessageOutputDebug 输出的信息在正式版本中将不会被移除.GUI 应用程序还可以使用 wxMessageOutputMessageBox 来即时显示消息,而不比象 wxLog 那样需要搜集(其它 Log 信息) 和等待(系统空闲),同样的还存在一个 wxMessageOutputLog 类,它将消息输出到 wxLogMessage。
和 wxLog 类似,wxMessageOutput 也有一个当前的目标,这个目标可以通过 wxMessageOutput::Set 设置,通过 wxMessageOutput::Get 获取.默认的目标是系统初始化的时候由 wxWidgets 设置的,在命令行程序中使用的是 wxMessageOutputStderr,在 GUI 程序中使用的是 wxMessageOutputMessageBox.wxWidgets 内部经常使用这个对象,比如在 wxCmdLineParser 类中,使用了下面的代码:
wxMessageOutput* msgOut = wxMessageOutput::Get();
if ( msgOut )
{
wxString usage = GetUsageString();
msgOut->Printf( wxT("%s%s"), usage.c_str(), errorMsg.c_str() );
}
else
{
wxFAIL_MSG( _T("no wxMessageOutput object?") );
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论