| Ricky: profil松江府乡民FotografieBlogSíť | Nápověda |
|
|
21.12.2007 Portal Charting:爱与恨交织 圣诞前夕,刚好手头上的活告一个段落,趁着空闲总结一下最近几个月来的工作。 RIA愈来愈成为人们讨论的热门话题,如果你还在告诉别人你的应用程序有一个功能和体积都非常彪悍的客户端,或者说你的Page还停留只是简单的使用AJAX通信然后展示一堆HTML结构的表格,那么对方一定会说你已经过时了。人们不希望继续部署桌面程序,同时又希望他们的浏览器里面运行着的玩意足够强大:丰富的窗口组件;可即时交互的图表;随心所欲的点击或者拖拉;……就像一个桌面程序,很矛盾不是吗? 过去的几个月里,我参与了公司下一代Portal产品的开发,所希望达到的目标正是这样。高级用户在客户端中编写工作流并发布成服务,用户在页面里面打开服务导入数据配置参数……运行得到结果以图表的方式展示,然后选择放弃、保存或者导出。我所承担的部分任务主要是可即时交互的图表,包括图表之间的联动。 公司的客户端产品里早已有了这些图表(IB),提供了非常丰富的功能,也可以选择其他第三方的图表引擎(比如很流行的JFreeChart),另外我们也在上海用比较短的时间重新实现了几种类型的图表。但是后来因为时间原因,绘图部分我们仍然首先重用了产品。回顾下来,我的感觉是JFreeChart的视觉效果较好,很干净很成熟,但是经过测试,在处理海量数据的时候,效率上没有什么优势,最关键的是需要编写大量代码将源数据根据参数重建模型提交给JFreeChart;IB的视觉效果比较简单,绘制大数据量的散点图时比较慢;重新实现的话视觉效果和效率都可以得到一定的提高,而且还可以学习JFreeChart,生成图像的同时生成AreaMap输出到页面,最大的问题是时间,要在短期内达到IB所具备的功能比较困难,加上后期IB做了一些优化提高了效率。因此最后还是选择IB作为默认的绘图引擎。 光有ChartEnginee还只是第一步,我们需要在页面中定义各种Widget,它们有自己的窗口、工具按钮、右键菜单,还能够在页面中拖放,最大化最小化或者关闭,甚至用户能够在图上面拖拉选区或者缩放……我们选择Ext作为整个Portal的客户端框架,中间也混用了dojo。在客户端里面,用户更改了参数或者直接在图上面操作后直接重绘即可,但是在浏览器里面我们必须把这个流程改成异步的,先以JSON数组的方式提交参数到服务器端的某个AJAX end-point,创建一个新的chart实例并且序列化在容器中返回一个序列号给浏览器,浏览器用这个序列号通知DOM对象请求图片,另外一个end-point直接将图像绘制到BufferedImage并写入字节流。 有了一个图还只是万里长征的第一步,然后是同一页面里多个图表之间的异步交互,各种事件的管理,选区里的数据保存在用户空间或者导出成各种格式的文档,还有服务器端的数据缓存以及对象的生存周期管理等等。另外一个重头戏是表格,可以说是最常用也是最难做好的控件,到了英国后我大部分时间都用在跟这里的同事一起开发表格控件上。Ext提供的表格还是比较让人满意的,在它的基础上,我们的表格控件已经具备非常好的交互性了,支持服务器端排序、分页、同步选中数据、自定义renderer…… 开始这个项目以前,我也经常写一些JavaScript代码,并且也常跟别人说作为一名开发者,应该掌握至少一种脚本语言,但是绝对没有像这次一样大规模的使用JavaScript来开发。虽然有JSEclipse插件和EditPlus来写代码,有FireBug来调试,但是遇到问题的频率和走过的弯路却感觉比以前从C++转向Java更加高,更加长。转载一下某同事的话说就是自从开始用JavaScript开发,感觉生活水平一下下降了好几个层次,大概这是每一个从桌面应用走向浏览器应用的开发者都要面临的问题吧。 几个月的痛苦和快乐,迷茫和振奋,期间整个团队还经历了一次从Ext1.0到Ext2.0的升级煎熬,现在运行在我面前的Portal Charting已经慢慢稳定下来,用我们开玩笑的话说就是Movie已经满天飞了,接下去要做的就是漫长的测试与除BUG。每当看到邮件里面的褒奖之词或者老板把访问者拉到我的电脑前的时候,总是会感到骄傲。毕竟,我在这个项目里面贡献了一份力量。 17.8.2006 ABC of OLAP/MDX 做PPT做到午夜两点多,居然再也睡不着了。躺在床上翻来覆去一小时,干脆再次爬起来看看能干点什么。
接受了一个研究OLAP/MDX的任务,给同事们作一个简单的报告。以前曾有读过SSAS的文档,但只是浅尝辄止,没有深入进去,这次也算是重新学习一下吧。
OLAP(联机分析处理)的概念是在上世纪90年代被提出的,主要是因为传统的OLTP(联机事务处理)已不能满足终端用户对数据库查询分析的需要,SQL对大型数据库进行的简单查询也不能满足终端用户分析的要求。用户的决策分析需要对关系数据库进行大量计算才能得到结果,而查询的结果并不能满足决策者提出的需求。因此,提出了多维数据库和多维分析的概念,即OLAP。简单的说就是数据不再仅仅是像关系数据库中的二维平面表一样仅仅只有行和列两个维度,行和列的交点处只有一个数据元素,而是可以有任意多个维度,形成一个超立方体或者多立方体。一个简单的例子就是在MSDN中的Cube:{Route, Service, Time, Measures}。
OLAP多维数据分析主要包括以下几种方法:切片/块、向上/下钻取、旋转/转轴。其目标就是从不同的角度在不同的细节程度上去观察数据,管理决策者以此为依据进行决策。
OLAP商业产品很多,微软主推的是它的Microsoft SQL Server 2005 Analysis Services (SSAS),IBM/Oracle/Informix/Sybase也都在自己的BI产品中集成了OLAP技术,和ETL、数据仓库、数据挖掘、前端工具等综合运用,形成一个完整的BI平台。
如同传统的关系数据库中SQL(结构化查询语言)是它的体系结构的基本构成部分一样,MDX(多维表达式)则是OLAP分析功能的利器,是一种功能完备、基于语句的脚本语言,用于定义、使用以及从多维对象中检索数据。它的形式也有点类似SQL,同样也包括DDL、DML、DCL等,但MDX不是SQL语言的扩展。事实上,MDX所提供的一些功能也可由SQL提供,尽管不是那么有效或直观。
一个典型的MDX查询语句如下:
select [SET] on Columns,
[SET] on Rows from [CUBE] where [TUPLE] 看看,是不是跟SQL很有几分接近。这里的Tuple、Set、Cube以及Measure、Member都是MDX里面的元素,具体定义也都可以查阅MSDN或者其他文档。就像SQL语句可以有很复杂的变化,MDX非常复杂,语法很烦琐,要熟练掌握还是需要很花一番功夫的。
在同事的推荐下,我在自己机器上安装了一个Tomcat+JPivot+Mondrian的环境用于测试学习MDX语法。JPivot是一个很好的前端展示工具,使用WCF (Web Component Framework) ,基于XML/XSLT来渲染Web UI组件,同时支持XMLA和Mondrian两种OLAP Server,而且JPivot和Mondrian都是开源项目。网上也能找到一些关于它们的介绍,不过中文的似乎就是一两篇文章被到处转来转去,如果希望有更加深入的了解,除了自己多摸索,还得去慢慢啃英文。 15.5.2006 JNI ActiveX最近这些日子对JNI(Java Native Interface)作了一个简单的研究,起因是需要将几个第三方的Win32应用程序整合进我们用Java开发的软件框架里面。虽然这样Java的可移植性打了折扣,但是软件开发,客户的需求是第一位的。好在Java提供了JNI这么一个接口供开发者调用其他语言编写的代码,而大量的Win32应用程序也都能够以自动化服务器或者ActiveX控件的方式供其他程序调用,这样两者之间就能够建立一个桥梁。 如果仅仅是为了针对某一个提供了COM接口的Win32应用程序与Java建立这么一个桥梁,事情就变得非常简单。只要获得Java窗口的Windows句柄,就可以利用这个句柄,在这个窗口上创建任何子窗口。将TypeLib导入到VC的工程中,可以获得ActiveX控件的C++包装类,然后直接调用类的方法就可以了。 我遇到的问题跟这个又有点不一样,目标是提供一个通用的接口,能够根据CLSID创建任何一个已注册的ActiveX控件,并且根据名称直接访问控件的方法和属性。对于这个接口的使用者,可能的使用方式如下: AxWindow wnd = new AxWindow("CLSID"); JNI方面的资料可以在J2SDK的文档中找到,生成C++头文件并且实现代码并编译得到DLL,在很多网站上都能找到简单的例子。但是MSDN上面对ActiveX控件的调用,大部分都是导入TypeLib得到C++包装类,可是如果仔细阅读代码,可以发现CComPtr类提供了这么几个成员函数: virtual HRESULT CComPtr<IDispatch>::GetIDsOfNames virtual HRESULT CComPtr<IDispatch>::Invoke; virtual HRESULT CComPtr<IDispatch>::GetPropertyByName virtual HRESULT CComPtr<IDispatch>::PutPropertyByName …… 原来那些包装类最终也都是调用这些函数:即先调用GetIDofName,通过方法或者属性的名称得到接口ID,然后通过标志位来控制是调用方法还是对属性进行读写。典型的调用过程如下: CComVariant varRet; // 先得到控件的IUNKNOWN接口 CComPtr<IDispatch> sp; pUnk->QueryInterface(IID_IDispatch, (void**)&sp); if (sp) { // 根据方法或者属性的名称得到DISPID DISPID dispid = 0; sp->GetIDsOfNames(IID_NULL, (LPOLESTR *)&lpszName, 1, 0, &dispid); // 预处理参数,参数均为VARIANT类型 DISPPARAMS dispParams = {NULL, NULL, 1, 1}; int nParams = m_ayParams.size(); if (nParams > 0) { dispParams.cArgs = nParams; dispParams.rgvarg = new VARIANTARG[nParams]; for (int n = 0; n < nParams; n++) { dispParams.rgvarg[n] = m_ayParams[n]; } } // 调用方法或者访问属性 wFlag可以为以下值之一 // 清理 m_ayParams.clear(); 还有需要解决的问题就是参数,首先是参数的个数和类型不确定,而且还需要首先将Java类型映射成C++类型或者反之。这个问题倒也不难,主要就是调用JNIEnv类的FindClass、GetMethodID、NewObject等函数,先获取类型,再得到该类型的构造函数id,最后通过构造函数在C++代码中创建一个对应的Java对象实例,或者是通过FindClass得到Java对象的类型,调用toString将对象的值统一转换成一个字符串,然后根据类型和值两个字符串创建一个C++对象。 最后还有一个让我郁闷的事情就是,由于在代码中使用了ATL,因此需要在目标机器上也安装有ATL80.dll。按照老习惯,我先选择“静态链接到ATL”重新编译一次,奇怪的是结果文件的大小并没有发生变化,运行的时候仍然报错:“未找到ATL80.DLL……”。最后不得已,只有在另外一台安装Visual Studio 6.0的机器上面重新编译发布。希望有经验的朋友能够帮助我解决这个问题。
25.4.2006 斧头C vs 锄头J除了中学时学了段BASIC,进入大学后一直都是学习C/C++的干活,大部分时间里也都是跟VC打交道,从最会利用向导写几个按钮触发函数,到后来深入到Win32应用程序的方方面面,积累了深厚的革命情谊。中间也有过一些类似为手机编写应用程序和实现该应用程序的Java Applet版本等的小插曲,可前者根本就是VC的另外一个版本,做那个Applet也是凭借一点基础概念一边学习一边实践。 虽然很明白,要成为一个合格的IT外来务工青年,斧头或者锄头,都只是工具只是手段,但是拿惯了斧头的手挥舞起锄头来,难免会有点生涩。当然,理想状态是左青龙右白虎,斧头锄头内外双修左右互搏……咳咳,废话就不多说了,开始操练,坚信:转型是必要的,前途是光明的。 的确很久没有更新了,一下子就到五一了,继续锄草。 27.2.2006 梅林会说话了要让梅林说话很简单,只需要安装上合适的TTS引擎就可以了,比如微软AGENT主页上面提供的一个叫做L&H引擎,并且在代码中设置一下语种ID就好了。可惜的是这个引擎尚不支持中文(同样是东亚语言,日文和韩文倒是支持,郁闷),要让这个控件能够说中文,还是需要安装完整的SAPI和相关的资源,这就过分了。 还有一个小进步就是支持脚本,要让梅林说更多的话做更多的动作不需要重新修改编译程序了,只需要编辑一个XML文档。而且如果想换一个角色,比如机器人、阿拉丁灯神、鹦鹉等也变得很方便了。 21.2.2006 魔法师梅林前些年浙大校网曾经有个黑客事件,某同学因为不能忍受校网中心的管理水平和办事效率,一怒之下攻入校网中心的Web服务器,修改了首页。不过此君大概也是个生性幽默之人,没有像时下动辄号称自己是黑客红客绿客的“高手”们一样在网页上破口大骂胡乱涂鸦,而是用一种让人意外的方式给所有人开了一个玩笑。 一登录该网站,屏幕上就跳出魔法师梅林,就是在常常搜索文件和Office助手里面出现的蓝袍白胡子老头。看着小老头摇头晃脑地抱怨使用校网过程中的种种不便,据说连“受害者”校网中心的负责老师们也忍俊不止。一看网页源代码,原来是用JavaScript插入了一个MS Agent控件,然后用脚本控制这个角色动作和说话。实现并不复杂,但是不得不佩服这个校友的创意,这位仁兄,现在早已毕业了吧,在何方高就呢? 后来我就一直记着这件事情,也曾经好好研究过MS Agent这个控件的用法,还在自己的一个程序里面聘请这个有趣的魔法师作为助手。只是有几个小小的问题,有些角色,比如Merlin,会在系统托盘栏中生成一个小图标。而我当时的那个邮件收发程序也是驻留在托盘栏中的,这下一个程序两个图标放在那里总觉得很别扭,得想个办法把这个图标给隐藏掉。 首先确认这个控件的C++包装类中没有提供关于托盘栏图标的方法,网上搜索也没有答案(Google也不是万能的),看来得自己动手强行干掉它了。方法很直接,就是调用Shell_NotifyIcon,问题就在于知道添加这个图标时候使用的窗口句柄和ID。请出Spy++查看桌面上与“Agent”相关的所有进程和窗口,呵呵,还真不少。联想到只有部分角色会生成这个图标,那么这里有一个注册名叫AgentCharacter的无标题窗口就很可疑。那么ID怎么办呢?呵呵,这里我用了一个很无赖的招数,那就是……一个个试,直到Shell_NotifyIcon函数返回TRUE,记录这个ID就是了。嘿,还真的成功了,重复几次确认每次都是用1025这个ID,看来这个方法是没错的了。 另外还有一个问题就是角色的有些动作是不会自动结束的,这样造成的结果就是如果长时间不去动这个角色,它可能会打瞌睡,到时候任凭你怎么点击拖动它都不会理你了。这个问题比较好解决,内置一个定时器,让角色无聊的时候随机做几个类似东张西望的动作即可。如果发现角色长时间处在睡眠、阅读的时候,点击或者拖动它的时候让它用不满的语气说上一句“找我有什么事?”,一个有趣的小老头,哈哈。 VB和JavaScript里面使用该控件都是比较简单的事情,如果使用VC可能要面对十几个名叫CAgentCtlXXX的类,未免繁琐了一些。所以我把这个控件重新封装成一个类以DLL的方式发布,只提供几个基本的方法:Create/Destroy、Show/Hide、Speak/Think/Play……这下使用起来方便多了。未来希望能够搞清楚TTS,使用语音进行交互,让这位老头为我们的程序做更多的事情。 23.12.2005 此long非彼long郁闷了两天的问题,有了一个郁闷的结果。把以前的一个程序重新用DLL封装供WebService(.NET)调用,结果总是失败。错误提示是很莫名其妙的一句话:“未将对象引用设置到对象的实例。” 原来的程序独立运行是良好的,重新封装后也只是提供唯一一个方法供调用,之前我在本地工程里面调用自然也是正常的,重新写一个HelloWorld式的DLL放上去也是正常的。难道是以前的版本里面有什么未发现的问题,这下紧张了。于是乎一遍遍的修改、测试、再修改、再测试…… 问题终于找到了,而且很简单,这个唯一的方法的C++原型需要传入多个long型整数作为参数,并且返回一个long型整数,而C#里面的long和Java一样是64位的,于是……。一时间我唯一的念头是想自杀。 16.9.2005 DIY性能监视器 做需要频繁处理大数据量的应用程序时对性能非常敏感,最直观的两个指标就是CPU占用率和内存使用量了。最简单的监控方法当然是打开系统自带的任务管理器,不过要在一排进程列表中找到自己感兴趣的一个或者几个进程,似乎不是很方便。比较专业一点的方法是使用系统管理工具里面的性能监视器,常用的、不常用的各种指标都能够在这里找到并且绘制出性能曲线来。不过,我不敢保证所有客户都能够或者愿意来进行设置,能够有一个性能监视整合到软件当中,是一个不错的方法。
还是去MSDN中查询,很快在Platform SDK文档中找到有专门的一个章节Performance Monitoring,剩下的事情就是仔细研读了。
(说句题外话,在微软的平台上作开发,阅读MSDN远远比在论坛中发问有用多了) 性能监控与计数主要有两种手段,一种是直接利用PDH函数,另外一种就是访问注册表。通过比较,我觉得只要基本熟悉一些系统定义好的结构,直接调用API比后者曲线救国要方便直观一些。基本流程可以归纳成下面几步: PdhOpenQuery 创建一个查询:参数是一个句柄指针 PdhAddCounter 添加一个计数器,参数包括前面的句柄,而且需要传递一个对计数器的描述字符串,比如需要监控某个进程的CPU占用率时的字符串如下: \Process(%s)\% Processor Time %s是进程名 需要监控某个进程的内存使用情况,字符串如下: \Process(%s)\Working Set %s是进程名 成功添加计数器后就可以开始查询了,分别调用 PdhCollectQueryData PdhGetFormattedCounterValue 前者收集查询数据,后者把数据按照制定格式填充到一个系统定义结构当中,最后访问这个结构就可以获得我们关心的性能指标了。如果查询失败,返回值将会是-1。 原理清楚了,就可以将这个流程封装起来成为一个类,放在一个独立的线程当中通过定时器定期查询一下软件的各项性能指标,以消息的形式抛给主界面所在线程以图表方式显示出来或者做其他处理都可以。 补充一下:系统中提供的指标还有很多很多,我们可以看看管理工具中的性能监视器,性能监视器中的指标名是什么,基本上也就知道添加计数器的时候的描述字符串是什么了。 15.9.2005 RPWT 这两天都是在跟软件的运行效率较劲,看着每次扫描数据的处理时间一点点缩短,心里无疑是开心的。但是这里还是给自己遗留下了一个问题,如果不能用我所能够认识的理论来解释的话,我只能把它归结为人品问题了。
问题描述
在一次完整的处理流程当中,包括了大量的文件读写。但是这些文件的读写应该说是没有什么直接关系的。因为在此之前,所有的数据都已经准备好了。 …… PrepareData(); // (1) ……
WriteData1(); // (2)
WriteData2(); // (3)
……
经过优化,上面三个步骤的运行时间都已经降到了几十毫秒这一数量级。特别是第一步的准备数据,每次运行的时间基本上波动不超过10毫秒,非常稳定。但是后面两个写文件的操作却发生比较大的波动,在50毫秒到100毫秒之间。不过总共加起来还是远远小于我一开始设定的最大值。
于是我把它们连在一起运行,奇怪的事情发生了。步骤1和步骤2的计时基本上没有什么变化,但是步骤3的运行时间一下子却变成了500多毫秒至1000毫秒,放大了近十倍。 问题出在哪里? 很明显,算法本身应该没有太大的潜力可以挖了,一千五百多个文件的写操作在50毫秒之内完成应该没有什么太大的问题。独立运行虽然有波动,但是绝对没有大到这个地步。文件冲突?不对啊,这根本就是不同路径下的文件,怎么会冲突呢。文件打开方式虽然有一个细微的差别,但是这个因素很快就被实际测试给否定了…… 问题的解决:诡异 郁闷了将近一个下午,问题却意外的得到了解决,但是原因我还是不知道。解决的方式很诡异,那就是把2和3给调换一下秩序!因为我无意中发现注释掉2后,3的运行时间马上变得正常了而且很稳定。然后把2给移到3的后面呢?有趣了,这下居然没有无谓的占用太多的时间。 天哪,难道真的是人品问题?需要补充说明的几点,2和3谁先运行谁后运行,在逻辑上一点差别都没有;另外一个有趣的现象是,把2移到3之后运行后,运行时间变成了稳定的100毫秒左右,但是此前经常能够达到最短的运行时间为50毫秒。 4.9.2005 文件实时监控我们的软件中需要对特定的文件夹进行监控,当数据文件的内容发生变化都需要发送一个消息通知上层的业务处理对象。VC中不像.NET那样可以轻松的创建一个FileSystemWatcher对象,但是一个VC程序员也应该能够很轻松的实现这样的功能。 首先查找MSDN,立刻就在File I/O Function中找到了如下一组API: HANDLE FindFirstChangeNotification( BOOL FindNextChangeNotification( BOOL FindCloseChangeNotification( 并且提供了一段使用样例(后来在网上发现的大部分资源其实也就是把这个例子翻译修改了一下),编译运行后发现基本上符合我的需要,只是有一点让我感觉到很奇怪。在调用第二个函数的时候,重新开始循环,在WaitForMultipleObject处直接pass过去,这样一次修改数据就会两次触发,这让我有点疑惑。 接下来的事情就简单多了,从CWinThread类派生一个CFileMonitor类,提供几个自定义消息:启动、暂停、关闭、通知,并且创建几个Event事件,线程类接收到启动消息后启动一个工作线程,通过对Find***ChangeNotify和WaitFor***Object等函数实现对文件夹的监控。 类的定义基本如下: class CFileMonitor : public CWinThread HANDLE m_hStart;
监控类自己的几个消息处理函数相对简单很多,无非就是设置一下事件状态,或者把消息转发给监控的调用者,如果是错误或者关闭消息,还要负责调用一下PostQuitMessage函数以退出线程。 监控类的调用者只需要定义一个CFileMonitor指针做为私有成员,在构造函数中启动这个线程类,设置一下需要监控的目录和接受通知消息的句柄,然后根据需要发送消息通知监控类器动或者暂停,并且添加一个消息响应函数用于处理WM_FILEMON_NOTIFY消息即可。当然,别忘了在调用者本身被销毁之前,要发送一个关闭消息给监控类,否则一来监控无法关闭,而且也将产生内存泄漏。 范例如下: CFileMonitor *pMonitor = 说明:我的一个朋友批评我的日志什么废话都有,就是不像一个软件行业人员。那么我以后就不定期写一些与我的本职工作相关的东西吧,当然不要期望这样的东西有太高技术含量,就当是给自己的进步留下点记号吧。 17.8.2005 工作要有计划 去过两次医院,打了5个小时吊针,领了各种退烧消炎药物一堆,燕子的身体情况好了起来,我的心情也没有那么紧张了。身体好了,工作还得继续。
前段时间燕子工作上遇到了一些小障碍,其实我工作的时候也碰到过类似的情况。现在,我在学习这么几个原则:
最后希望亲人朋友们都注意身体,夏天多吃水果多喝水。好的身体是革命的本钱。
题外话:昨晚回家时发现路边自来水管道漏水,打电话给自来水公司,也算是为建设和谐社会,节约资源做贡献吧。 15.7.2005 XML动态工具栏 既然菜单、页面、状态栏都是由XML配置动态生成的,工具栏也不例外。
我理想中的工具栏应该是同时支持两种形式:一种就是最常见的普通工具栏,包括普通小按钮、Check风格的小按钮、带下拉小箭头可弹出菜单的小按钮和分隔符;另外就是类似IE的ReBar了,所有能够出现在对话框或者页面上的控件可以加载,而且在一个页面下,可以自由配置出多个ReBar(0~N,只要屏幕上放得下……)。 首先就是要把XML配置定义出来,一个典型的配置如下(限于篇幅,每个控件下的Command子节点以及布局信息省略): <ToolBars>
<SystemBar id="3100" caption="系统工具栏"> <Button id="3101" text="按钮1" image="0" /> <Separator /> <Check id="3102" text="按钮1" image="1" /> <DropList id="3103" text="按钮6" image="7"> <Commands /> <MenuItem /> </DropList> </SystemBar> <PageBar id="3200" caption="页面工具栏1"> <Control id="3201" class="Text" caption="标题:" /> </PageBar> <PageBar id="3300" caption="页面工具栏2"> <Control id="3301" class="Check" caption="复选" /> </PageBar> </ToolBars> 上述配置定义了一个普通工具栏和两个ReBar,前者加载四种类型的工具栏按钮各一个,其中DropList还带有下拉菜单的配置;后两者分别放置了一个Static文本和一个复选框。
效果如图所示,嘿嘿,似乎还不错,至少功能上达到了,至于外观,那就请控件的设计者们考虑如何实现OWNER_DRAW风格吧。 还有一点,普通工具栏上面放置了很多小图标,既然是都做成可配置的,当然不希望把这些图标都在程序资源中定死。万一某天上司希望加一个新功能按钮,而没有合适的图标,我岂不是要哭了?不过这也简单,写一个小程序编辑图片,生成一个CImageList对象,然后把这个对象序列化成一个dat文件,让我的工具栏加载的时候读取这个dat文件加载这个CImageList对象不就可以了吗?这个dat文件也成为了配置的一部分,这样再遇到上面那种情况也只需要重新发布一下dat文件就好了。 1.6.2005 XML动态界面上次提到的XML动态界面基本上已经完成了,效果如图所示。整个程序采用MFC的多文档结构(MDI),但是在此基础上作了不小的手术。通过派生自己的文档管理对象(CDocMgrEx)、文档模板(CDocTemplateEx),并提供了一个框架管理对象(CFrameManager)统一管理视图、子窗口、控件、菜单、状态栏的创建、布局,甚至消息传递,这样做的最终目的无非是希望能够把程序的界面管理能够根据配置文件动态的变化,而不用不停的重新修改程序代码。 效果图中的界面就是完全由页面配置动态生成的。在配置中,每个功能页面按照三个层次进行组织。一般页面分为四个像限,分别是数据项目(左上)、目标范围(左下)、数据表格(右上)和高级图表(右下)。当然,这个布局也是可以根据配置自由组合的,可以根据需要定义某个页面由两个或者三个,甚至一个面板组成,而且组合的时候也不一定按照四个像限的方式。在面板上,根据需要的功能,布置不同类型的控件。这样就构成了页面-面板-控件的三个层次。 这样还有一个问题:这样的配置只包含了界面的布局信息,但是界面上的各个部分的操作逻辑还没有很好的组合在一起。比如用户按了一个按钮或者是选择了下拉列表中的某一项,将会发生什么,在配置中并没有得到反映。难道要把这些东西写到代码中去吗?我当然不希望这种事情的发生。因此配置信息中还多了一个层次,就是操作。控件中除了包含类型和布局外,还把需要处理的操作当作子节点也保存在配置里面了,包括:需要响应的操作、目标窗口的id以及附加数据。而框架管理对象也多了一个职责,它需要负责收集这些消息,当某个子窗口或者控件上进行了某个操作,将会被框架管理对象捕捉到,根据操作的类型和目标ID,查找到目标窗口或者控件,发送消息,通知对方做响应的处理。 简化后的配置如下: <Page id="6000" class="FormView" caption="数据浏览器" toolbar="4010;4020;"> 28.5.2005 XML动态菜单我正在开发的程序经常需要根据客户的需要或者市场的变化对功能进行增删,或者说对界面进行微调。如果说因此频繁的升级客户端程序,不停的发布新程序将会是一件很痛苦的事情,用户也会感到厌烦。但是注意到客户端大部分的作用体现在收集用户的输入,然后从服务器获取数据并显示,就像一个浏览器……慢着,浏览器?为什么不做成一个浏览器呢?除了下载应用数据,客户端的功能、界面描述都做成配置文件,全部都从服务器下载,这样只要修改服务器端的配置,就可以让客户端上显示的功能界面发生改变。于是,XML出场了…… 这一次我先说说菜单部分: [美化我的菜单]
23.5.2005 聚类、遗传算法、ClusterProject作为燕子的硕士研究生毕业论文设计,这算是我和她合作完成的一个“项目”。其中我负责的是总体框架设计和大部分用户界面相关的代码,特别是图像处理部分。实际的算法是由燕子完成的,毕竟,应用数学硕士研究生的专业知识不是我能够在短期内能够掌握的。今天在这里对一年多前的工作做一个回顾。 本程序用于聚类算法计算以及图形模拟。 主要特性包括: 1、多文档-多线程结构,可同时处理多份数据。 程序的结构不算很复杂,基本工作可以分成以下几块: 1、算法 CClusterBase┬→CHierarchical┬→CAgglomerative┬→CAverageLinkage 这样就得到一个算法继承体系。在基类中定义一系列的虚接口和一些工具方法。子类中提供方法的具体实现,由燕子来实现。 class CClusterBase 各种距离计算方法和结果评估指标也是同样实现的。 用户新建一个空项目后,导入原始数据。打开项目配置,选择具体的算法,根据算法的不同搭配选择不同的参数,这些信息都会被保存在项目文件当中。当用户选择RUN的时候,程序搜集这些信息,通过Factory模式来产生一个具体的算法实例对象,完成计算并且生成各种报表和图像数据文件。用户可以通过项目视图中的图标来浏览这些图文报表。 2、图形 最早只是为燕子做了一个简单的独立小程序,把原始数据降维后得到一个二维平面分布或者三维空间分布的图像,可以比较直观的表现聚类的效果。后来慢慢的产生了一个想法,把功能进行扩充,除了提供分布图像以外,还要提供平面点阵、树状图、折线图等,并且提供缩放、图例显隐、颜色调节、导出等功能,并把这些功能封装成控件加入到软件里面来。 燕子的算法类在计算完成后会生成若干个不同类型的数据文件,这些文件的数据格式都是经过定义的。图像控件读取这些数据文件自动完成绘制,并且能够导出成BMP位图文件或者通过PS文件生成PDF文件。 毕竟是1年多前做的程序,现在看起来,不足(甚至可以说是设计拙劣)的地方还是有不少的。燕子也希望我能够做一些改进,并且提出了具体的修改意见。我将会在稍微空闲的时候开始这项工作。 更多详情见相册中的ClusterProject。
9.5.2005 给自己的职业规划 工作已经两年了,应该对自己的职业规划有个更明确的认识了。 |
|
|