Vista 版 Folding@Home 用户统计小工具






3.75/5 (3投票s)
2007年3月29日
5分钟阅读

37580

135
一篇关于监视 Folding@Home 用户统计信息的 Gadget 的文章。

引言
用于 Vista 的 Folding@Home 用户统计小工具访问 ExtremeOverClockers 的 Folding 网站的 XML 数据馈送。它以一种类似于 EOC 网站提供的 sigimages 的方式格式化 XML。
背景
Folding@Home 是斯坦福大学 Pande 实验室运行的一个分布式计算项目。您可以在项目网站上找到更多信息。
使用代码
提供的代码最初是为 Vista 小工具编写的,因此可以像任何其他 Vista 小工具一样使用。在功能上,它是一个用 JavaScript 编写的简单 XML 解析器,它收集、识别并将值分配给 HTML 中的相应位置。
它被设计成可移植的;因此,小工具设置范式被封装起来,并且可以被其他设置存储方法所取代。已有两个想法是将它嵌入到谷歌的桌面小工具中,或者将其实现到 Konfabulator 中。
视觉表示非常简单——它使用一个表格来组织我们将从 XML 数据源获取的内容。表格中的项目是以 XML 中预期的值命名的。背景图像是来自Folding@Home 网站的一张图像的裁剪和添加了 alpha 通道的版本。
<body background="fah.png" onload="showXML();interval = setInterval('showXML()', 900000);"
onunload="clearInterval(interval);">
<table id="gadgetContent">
<tr>
<th colspan=2><b><span id="User_Name" /></b></th>
</tr>
<tr>
<td><u>Team Rank</u>:<br><span id="Team_Rank" /></td>
<td><u>Points</u>:<br><span id="Points" /></td>
</tr>
<tr>
<td><u>Change 7d</u>:<br><span id="Change_Rank_7days" /></td>
<td><u>24hr Avg</u>:<br><span id="Points_24hr_Avg" /></td>
</tr>
<tr>
<td><u>User Rank</u>:<br><span id="Overall_Rank" /></td>
<td><u>Today</u>:<br><span id="Points_Today" /></td>
</tr>
</table>
<span id="errorText" />
</body>
我使用了 setInterval
而不是 setTimeout
。性能没有差异,而且我更喜欢这个名字。
我认为错误处理很重要,所以我将在实际功能之前提及这一点。
该小工具的错误处理由主进程 showXML()
管理,但有一些辅助函数用于管理界面。此外,用于获取小工具设置的辅助函数不处理自己的错误。相反,如果它识别出任何问题,它就会将责任推给主进程。我随意地使用了 RangeError
异常,但没有任何好的理由。
function setGadgetProperties()
{
username = System.Gadget.Settings.read("username");
teamID = System.Gadget.Settings.read("teamID");
if ( username.length == 0 || teamID.length == 0)
throw RangeError("Have you set your options?");
}
function displayError(message)
{
errorText.innerHTML = message;
gadgetContent.style.display = "none";
}
function clearError()
{
errorText.innerText = "";
gadgetContent.style.display = "";
}
ShowXML()
中的主要代码块完全包装在 try
块中。catch
在下面。鉴于此小工具的目标受众可能不具备任何特定的技术能力,我希望保持消息的简洁明了,但清楚地说明可能发生的情况。
if ( e instanceof TypeError )
{
if ( e.message.indexOf("documentElement") >= 0 )
displayError("Did you spell your name right?");
else if ( e.message.indexOf("resource") >= 0 )
displayError("Are you connected to the internet?");
else
displayError("Quick, tell someone!<br>" + e.message);
}
else if ( e instanceof RangeError )
displayError(e.message);
else
displayError("Something's wrong.<br>Restart this gadget instance.");
排除以上内容后,我们来看 ShowXML()
。我们将分三部分介绍。
第一部分:获取数据
EOC 的 Jason 会定期从 Folding@Home 报告服务器拉取数据,并将数据整理成可用的格式。他提供了一个 XML 数据源供大家使用,所以这就是我们小工具要利用的。要获取用户想看的数据,我们首先需要获取用户的输入,这样我们才能告诉 Jason 我们想要什么信息。
这里没什么复杂的操作,但请参见“兴趣点 #1”了解您可能不知道的关于 MSXML2.XMLHTTP
的知识。
//capture username & team from settings
setGadgetProperties();
clearError();
//get the data stream from EOC
xmlhttp.open("GET", http://folding.extremeoverclocking.com/xml/user_summary.php?un=
+ username + "&t=" + teamID,false);
xmlhttp.send(null);
第二部分:组织数据
XML 返回 Team
和 User
信息。我只对用户信息感兴趣,所以我只捕获了这些。但是,没有理由不允许某人也捕获团队信息,并且可以轻松编写类似的 insertTeamXML()
函数来为团队表格做同样的事情。
//create the base XML doc
xmldoc = xmlhttp.responseXML;
//capture only the user XML
userxml.async = false;
userxml.loadXML(xmldoc.documentElement.getElementsByTagName("user")[0].xml);
//fill in the table
insertXML(userxml.documentElement);
第三部分:显示数据
insertUserXML()
封装了 gadgetContent
表格的操作。注意:如果有一种更好的方法可以根据 HTML 中的 span ID 来捕获 XML 中的数据,我很想了解——越灵活越好。
//set username & username size
User_Name.innerText = data.getElementsByTagName("User_Name")[0].text;
if ( User_Name.innerText.length < 10 )
User_Name.style.fontSize = 'medium';
else if ( User_Name.innerText.length < 16 )
User_Name.style.fontSize = 'small';
else if ( User_Name.innerText.length < 22 )
User_Name.style.fontSize = 'x-small';
//fill in data
Team_Rank.innerText = data.getElementsByTagName("Team_Rank")[0].text;
Points.innerText = data.getElementsByTagName("Points")[0].text;
Change_Rank_7days.innerText = data.getElementsByTagName("Change_Rank_7days")[0].text;
Points_24hr_Avg.innerText = data.getElementsByTagName("Points_24hr_Avg")[0].text;
Overall_Rank.innerText = data.getElementsByTagName("Overall_Rank")[0].text;
Points_Today.innerText = data.getElementsByTagName("Points_Today")[0].text;
//tweak data
if ( Change_Rank_7days.innerText.charAt(0) != '-'
&& Change_Rank_7days.innerText.charAt(0) != '0')
{
Change_Rank_7days.innerText = "+" + Change_Rank_7days.innerText;
}
在所有当前情况下(尽管 PS3 用户最终可能会证明这一点是错误的……),所有值的预设字体大小都可以正常工作。但是,由于用户名的变化非常大,我不得不确保用户名能正确显示;为此,我“猜”了一个最佳的名称大小。到目前为止,我的猜测似乎还不错。
此外,我还想模仿 EOC 生成的论坛 sigimages 中 Change_Rank_7days
的 +/- 显示。这纯粹是为了美观,但我认为如果省略了它,将会错过。
设置页面非常无聊,几乎不值得一提。所以我把它留到了最后。然而,这是进行 Vista 特定编码的地方,所以我们稍微看一下。
<body onload="showSettings();">
<label for="username">Username:</label><br>
<input type="text" name="username" id="username" length="40" /><br>
<label for="teamID">Team ID#:</label><br>
<input type="text" name="teamID" id="teamID" length="40" />
</body>
正文只有两个用户输入字段。当小工具首次启动时,用户必须先来这里设置他们的信息。但是,一旦信息设置好,只要他们不手动关闭小工具,这些信息就会在任何后续的关机或重启后得到保留。有关更多信息,请参阅“兴趣点 #2”。
下面的脚本简单地展示了当窗口打开时(参见 HTML body 中的 onload 事件)如何加载小工具设置,以及在窗口关闭时如何保存(注意顶部的事件处理程序赋值)。
System.Gadget.onSettingsClosing = settingsClosing;
function showSettings()
{
username.value = System.Gadget.Settings.read("username");
teamID.value = System.Gadget.Settings.read("teamID");
}
function settingsClosing(event)
{
if (event.closeAction == event.Action.commit)
{
System.Gadget.Settings.write("username", username.value);
System.Gadget.Settings.write("teamID", teamID.value);
}
}
关注点
MSXML2.XMLHTTP
对象会缓存其结果。这有好有坏,因为当用户的互联网服务提供商无法访问时,缓存可能会导致混淆。- Vista 在 Windows 侧边栏目录中以 Settings.ini 文件存储当前运行的小工具的配置列表。这些设置对每个用户都是特定的。这很可能是
System.Gadget.Settings
接口读取/写入的物理位置。
历史
- 1.1.1 - 为 CodeProject 进行的少量修改 - 无功能性更改
- 1.1.0 - 改进了错误处理(感谢 totoro 的测试和反馈!)
- 1.0.0 - 发布到 TechReport 的分布式计算论坛(加油,2630 战队!)