九天雁翎的博客
如果你想在软件业获得成功,就使用你知道的最强大的语言,用它解决你知道的最难的问题,并且等待竞争对手的经理做出自甘平庸的选择。 -- Paul Graham

【转】为什么我认为每个程序员都应该用Mac OS X?

原文:http://tiny4.org/blog/2010/02/why-programmers-should-use-mac-os-x/

查尔斯·狄更斯老师的《双城记》里有句非常著名的话,我每次看到都心潮澎湃,所以看了无数次《双城记》总是在那两句话前后打转。心说,开头就这么
好,正文无论如何也无法让你觉得精彩了吧?

原文是,“It was the best of times, it was the worst of times, it was the
age of wisdom, it was the age of foolishness, it was the epoch of
belief, it was the epoch of incredulity, it was the season of Light, it
was the season of Darkness, it was the spring of hope, it was the winter
of despair, we had everything before us, we had nothing before us, we
were all going direct to Heaven, we were all going direct the other
way–in short, the period was so far like the present period, that some
of its noisiest authorities insisted on its being received, for good or
for evil, in the superlative degree of comparison only. ”

译做:“那是最美好的时代,那是最糟糕的时代;那是智慧的年头,那是愚昧的年头;那是信仰的时期,那是怀疑的时期;那是光明的季节,那是黑暗的季
节;那是希望的春天,那是失望的冬天;我们拥有一切,我们一无所有;我们全都在直奔天堂,我们全都在直奔相反的方向–简而言之,那时跟现在非常相象,某些
最喧嚣的权威坚持要用形容词的最高级来形容它。说它好,是最高级的;说它不好,也是最高级的。”

我认为对于程序员来说,我们正在经历最美好的时代,当然也是最糟糕的时代。

美好的原因在于,这是一个充满机会的年代,近十年每次我们以为世界一切尘埃落定的时候,就会出现新的惊喜。如youtube、facebook、
twitter等等,甚至包括了iPhone以及即将上市的iPad。世界上无数聪明和勤奋的人们正在努力,告诉我们一切还早,我们还活在一个迅速发展着
的世界。只要世界还在迅速发展,谁知道你会不会就是下一个twitter、下一个iPhone的发明者?甚至是下一个马化腾,虽然现在腾讯如日中天,但是
如果时间倒流到99年,甚至01年,大多数心中可能还是会觉得那只是个玩物吧?

然而,糟糕其实也正在于此,前面的机会多,陷阱就多。人们天生就是风险躲避者(某个名人或者是名著里面说的,可惜记不清了,无法拿来掉书袋了),这
也许就是独裁百试百灵的原因。人们总害怕错误的选择,所以变得犹豫起来。现在这样的时代,你就有无数可以犹豫的事情,因为很多事情还在发展中,不知道谁会
笑到最后。最近仍在胶着的战斗很多,如,Wifi和3G(我认为赌局还没开场),微软和Google,微软和苹果,苹果和Google,苹果和Nokia
等等等等。

这些战斗的结果,对普通用户来说,只是买什么产品,用什么产品的问题。对开发者来说则是抱谁的的大腿,跟谁混的大事儿。

所以我们这些久经沙场而没有发达的家伙,总是对面前的选择感到胆战心惊,甚至养成了父母问你吃什么都会大发雷霆,大喊“不知道”的选择恐惧症(我的
症状非常严重,出去吃,绝不点菜,在家里一律随便。但也有可能是看Monk看多了闹的)。

之前我和赵姐夫(@Jeffz_cn)
霍炬(@virushuo)
方块(@KrzyCube)
还有很多人在twitter上面大侃过一
次这个问题,有兴趣的可以看看Cat Chen的评论
、以及
姐夫的整理

、和方块的整理
(我
们的讨论很长,但还算有趣吧)。

现在,我已经远离了Windows阵营,做Web开发用Php,做客户端只做Mac客户端软件,还做一些iPhone开发。我认为每个程序员都应该
至少用一用Mac OS X。

原因如下:

1、操作系统的竞争到了一个新的阶段

Windows虽然仍旧是实质上的老大,但是操作系统的战争早已不是10年前的样子。随着Web应用的大行其道,很多人的电脑,都已经成了名符其实
的上网本或者上网机。

Window/Mac OS
X/Linux/Unix几大主流平台上都有了流行的Web浏览器,只要你不是一个执着的IE粉丝,那么如果你的主要任务是Web浏览,现在几大操作系统
的体验已经非常接近了。其他的基于互联网的流行软件,一般也都有比较好的跨平台支持。例如,聊天工具,Gtalk全平台覆盖,Msn全平台覆盖,现在甚至
连一向被人看作很封闭的QQ,实际上也有Linux版本(虽然口碑很大,但是还有lumaQQ这样的替代产品),Mac版本(口碑不错),iPhone版
本了(最近刚更新过,很不错)。

我们甚至看到了上网本这一硬件类别的产生。互联网成了计算机的中心,这很大程度上降低了微软的垄断优势。

3-4年前,我切换到Mac OS
X的时候,除了招商银行等少数应用以外,我没有觉得任何的痛苦。那时候,我主要开发服务器端程序,程序的运行环境是Linux,在Mac OS
X这个Unix上面开发,其实很方便。

程序员可以在不同平台切换并不重要,重要的是,我们这些程序员的衣食父母已经可以自由的在各种平台上切换了。这就给了我们去尝试Windows以外
平台的可能性。

很多人在比较Windows和Mac OS X的时候,有一个误区。因为他们自己是长期的Windows用户,对很多Mac OS
X的细节很不喜欢(右键、按钮位置等等)。但是作为一个开发者,你要注意的是用户。我希望你们设想一个场景,一个从来没有用过电脑的人,他们学习
Windows和Mac OS X,他们会更喜欢什么。我想更多人会喜欢Mac OS X,如果我的设想是对的,那么未来是属于Mac OS X的。

2、iPhone以及未来的iPad

iPhone是近10年来,我看到的最革命性的硬件产品。虽然他的功能,除了多点触摸、加速度传感器以外,大多数我们都在Nokia和其他竞争者的
产品中见到过。我用过Nokia2002年的产品7650,iPhone的所有功能它几乎都有(没有wifi,但是有gprs),但是用起来跟
iPhone就是不一样。

那么区别在哪里呢?05年我买iPod
Nano的时候,看到盒子的时候,我吃了一惊,因为盒子最显眼的地方写的不是“容量:4G”,而是“可以容纳1000首歌,xxx张照片”。这家卖水果的
公司突然让我明白,用户其实关心的是那个叫mp3的东西可以容纳多少歌,而不是从比特的角度,从存储的角度他的容量是多少G。多少G多少M的容量,是实现
“容纳多少多少歌”这个目的的一种手段。我们总是太过care手段,忘了我们的目的是什么。

做产品的时候,很多人也是这样。如果你是第一次看电视购物卖手机,也许你就会想把手里面的iPhone扔掉,去买那些垃圾。因为你听介绍的时候,你
会觉得全世界最好的手机就在那电视里,叫做“XX全能王”。从功能列表来看,它们简直是世界上最好的产品,但是如果你真的拿到手,你也许会发现那东西3分
钟死机,也许什么操作都很慢,也许摔了一下就坏了。那个时候,我估计你会领悟到,功能多当然好。但是功能列表不会包括“不会3分钟死机”,“操作不会很
慢”,“摔一下不会坏”。手机,或者任何产品对你最重要的不是功能多,而是好用。

iPhone让我发现原来手机上也可以有这么好用的浏览器。这是个纯技术问题么?不是,要知道Nokia很多手机用的浏览器内核和iPhone毫无
二致,都是Webkit。但是操作起来看看,大家知道iPhone出来没有多久,所有网站手机流量最大的就是iPhone了,这不是靠运气啊。我有个
Nokia
E61i欧版手机,可以用wifi,但是用它的浏览器,实在是不方便,每次都要重新选择wifi,打开网站也很慢,打开了以后,放大缩小也很不方便。

好的体验仅是其一。更重要的是水果公司在iPhone这个产品上开发了AppStore,这是近10年来,我认为在软件行业最重要的一个东西。1976年2月3日比尔盖茨发表了著名的《An
Open Letter to hobbyists》(给爱好者们的一封信)

,事实上开启了商业软件的道路。然而,商业软件从诞生起,就受到盗版
的诅咒。从全球看,表面上微软是盗版的最大受害者。没错,从金额上看,是这样的。但是实际上所有的第三方开发者,才是盗版的最大受害者,他们中的很多人因
为盗版而丧失了成长为微软这样企业的机会。

AppStore不能完全杜绝盗版,但是它从发行和支付渠道出发(跟iTune
Store如出一辙),很大程度降低了盗版对第三方开发者成长带来的风险。很多单枪匹马的第三方开发者,凭借做iPhone软件发家(当然一个市场是有容
量的,你不能指望它超越自己的机极限)。很多开发Mac软件多年的人,没因为开发Mac软件发家,但是因为移植了一个简单版本到iPhone发了家。据
说,因为开发iPhone只能用Mac OS X,这成了近两年对Mac电脑销售最大的拉动。很多人为了开发iPhone程序而购买Mac电脑。

就在这时iPad来了。价格在2000-3000之间的上网本,应该是最近和未来,电脑市场一个很重要的增长点。这个价位可以吸引很多买不起电脑
(可能更多是不舍得买电脑)的人。另外,关注上网这个概念简化操作,也可以让很多觉得电脑太难用的人们,来购买电脑。可惜,基于Windows的上网本在
用户体验上实在是乏善可陈,往往仅仅是一个屏幕更小的Windows而已。

Windows在PC上面的优势本来有机会拓展到其他平台。在WinCE和Palm,微软实际上获胜了。但是之后就固步自封了,现在iPhone出
来了,WinCE实际上已经没有什么大机会了。上网本和触摸设备,本来也是Windows有巨大优势的,但是微软实在缺乏在用户体验方面的努力(你去看看
各种基于Windows的触摸查询设备吧,居然需要点一下然后鼠标光标蹦过来,然后才能点!!!!!!!)。

iPad的发布会,并不让人多么惊喜。这是因为iPad和iPhone战略在水果公司是紧密结合的,iPad包含了iPhone的所有革命性,然后
把尺寸放大。发布会开到结尾,说到电池时间和价格的时候,我才开始兴奋。

iPad意味着什么?意味着一台便宜的上网本;有电脑的用户也不介意购买一个,买不起或者不舍得买电脑的人可能会买;因为便宜,所以医院和餐馆等可
能会购买作为内部管理系统(医院信息系统,点餐系统);学校可以买做教育用途(电子书包)。意味着,我们可以在电脑上面写文档累了的时候,躺在床上抱着它
听歌,看看书,玩个游戏。

这个产品如果操作的好的话,可以造就一个比iPhone更大更好的软件市场。

3、Mac OS X是Unix

对于大多数做服务器端开发的开发者,服务器端开发也许就意味着Linux下的C/C++和Java。Mac OS
X是真正的Unix(符合标准,有授权,合法的可以用Unix商标)。大多数这类开发,你可以在Mac OS
X下做,在Linux下执行。少数涉及到Linux和BSD内核不同的应用,稍微麻烦一点。但是如果你做到这个级别的话,我想你也不在乎了。

大多数基于Linux的开源软件,大多数为Unix架构设计的程序,都可以在Mac OS
X下正常工作。虽然有很多软件编译起来,会遇到很多稀奇古怪的问题,但是放心,你在不同的Linux版本间编译的时候遇到的会更多。虽然Mac OS
X下面的自动软件安装工具也会有各种各样的问题,但是放心,Linux下的也是。

Unix文化最经典的管道和脚本,Mac OS X也支持的很好。

Mac OS
X是一个非常好的平衡,Unix式的命令行、脚本能力它全部具备,而且带有一个非常好用高效的GUI。所以不管你是CLI(命令行界面)控,还是GUI
控,你都应该可以玩的很好。

有人问我Mac OS X是不是一个很好的学习Linux的途径。我不得不说,你要是真的玩转了Mac OS
X(CLI+GUI)的话,你真的无需去学习Linux,用就是了。尤其是,学习Linux是为了学习服务器管理的人们,真玩转了Mac OS
X,Linux有什么区别?

4、开拓你的视野

一段时间以来,
研究自己发展的过程中的瓶颈的时候,发现一切瓶颈都不来自能力,都来自视野,来自于给自己设置的愚蠢的极限

。这么说,是因为我发现,我想做一辈子
的程序员,而我也乐意学习新的知识,我也确实擅长不停的学习新的知识。

只是每经一个阶段,一旦我发现自己长期没有什么进步的时候,就会发现原因就是我已经不知道前面的路在哪里了。在.net出现之前,我已经是一个微软
几乎所有产品的专家(但我不是MVP
),
我可以不借助ATL自己写Com组件,我写过IE和Outlook插件,等等。但是到了.net时代,我的心疲惫了,我决心不再跟随微软的脚步。

这两年,尤其是09年,做了一些iPhone开发,Cocoa开发
(Tiny4Reader)

,做了些服务器端开发(GinkgoTek.com),我又开始像刚开始接触电脑那样的激情澎湃了。

对大多数纯Windows程序员,Mac OS
X的GUI和基于Unix的CLI都是新的天地,哪怕你不真的从事这两方面的开发。在这些领域看到的东西对你的开发都是有利的。

腾讯是一个大家印象中很不注重用户体验的公司(老程序员视角)吧?但是现在QQ已经有了Mac版,iPhone版,而且iPhone版本已经多次打
磨,已经是一个相当好用的iPhone应用了。年前我去了趟深圳,见了他们的iPhone开发team,他们很重视用户体验,也做了很多的尝试。而腾讯更
多其他平台开发的工程师,也开始更加关注用户体验了。

对绝大多数Windows程序员,关注用户体验的经验和意识都不足,使用Mac OS X有利于提高这些方面的素养。

最近我在参与翻译《Cocoa® Programming Developer’s Handbook》,从中知道,我最崇拜的Tim Berners-Lee爵士

就是在Mac OS
X的前身,NeXTSTEP操作系统(NeXT公司产品,乔布斯创建的公司,后来被买回水果公司,这个操作系统和原有MacOS一起,成了Mac OS
X的前身)下开发的人类第一个浏览器WorldWideWeb的,打开了人类的Web时代的。

附,本文引发的一些相关的讨论:

 

不尽同意原文观点,但是比较欣赏作者对水果的热爱

阅读全文....

MacOS中的全局快捷键设定 With Objective C

write by 九天雁翎(JTianLing) -- www.jtianling.com

讨论新闻组及文件

    全局快捷键属于比较有用的功能了,在Windows下使用RegisterHotkey可以很方便的设定,(指的是通过程序设定啊)Qt中没有封装此功能,所以稍微麻烦一点,可以参看我原来的文章《Qt/PyQt中使用系统全局的快捷键》,换到了MacOS中后,又得重新学习了,真是悲哀。。。。。。。
    搜遍互联网,才总算发现有用的文章,《Program Global Hotkeys in Cocoa Easily》一文真是精华中的精华。不仅说明了怎么在MacOS中设定全局快捷键,而且超额的指点了怎么让用户在程序中自定义。。。。强就一个字,作者绝对是介于牛A及牛C之间的人物。
    唯一还有点遗憾的是,作者虽然是说用Cocoa,但是其实使用了carbon框架,而carbon框架使用的还是C语言的接口,并且有回调函数的设置,导致接口使用上不能用纯Objective C。。。。。。
    本文仅记录大致使用流程,作为备档,详细的接口意义及各类,结构的意义未作详细说明(文档中我都没有查到较为详细的说明)。

准备工作

如上所述,用了Carbon框架,所以首先得为工程添加框架得链接依赖,然后使用上包含Carbon/Carbon.h文件。

注册全局快捷键回调函数

回调函数的原型如下:
OSStatus MyHotKeyHandler(EventHandlerCallRef nextHandler,EventRef theEvent,
                                                 void *userData) {
}

注册的方式

        EventTypeSpec eventType;
        eventType.eventClass=kEventClassKeyboard;
        eventType.eventKind=kEventHotKeyPressed;
        InstallApplicationEventHandler(MyHotKeyHandler, 1, &eventType;,NULL, NULL);
主要的函数是InstallApplicationEventHandler,表示注册相应的回调函数,作为C语言接口,回调的函数原型必须一致。

注册快捷键


        EventHotKeyRef gMyHotKeyRef;
        EventHotKeyID gMyHotKeyID;
        gMyHotKeyID.signature='rt2h';
        gMyHotKeyID.id=1;
        
        RegisterEventHotKey(6, controlKey, gMyHotKeyID,
                                                GetApplicationEventTarget(), 0, &gMyHotKeyRef;);
        
        gMyHotKeyID.signature='te2h';
        gMyHotKeyID.id=2;
        
        RegisterEventHotKey(7, controlKey, gMyHotKeyID,
                                                GetApplicationEventTarget(), 0, &gMyHotKeyRef;);
注册快捷键,RegisterEventHotKey是整个过程中最重要的接口,第一个参数的数字,代表了最终响应的键值,这里有点奇怪,不是像Windows中那样使用表示虚拟键值的宏,而是直接用按键代表的数字来表示,而且此数字甚为奇怪,我也很为纳闷。比如上面的6,7分别表示Z键和X键。这些数字我只有在《Program Global Hotkeys in Cocoa Easily》一文中提到的AsyncKeys软件中我才能正确的知道。此软件运行时,如下图:

上面就是我按Z键时,显示的样子,其中我们需要设置的值为AsyncKey Number那一栏的值,即6.。。。。。。。。原因不明,没有深入了解。
RegisterEventHotKey第二参数是控制键的设置参数,分别可以为cmdKey, shiftKey, optionKey,controlKey,各自的意思我想就不用我讲了,不过需要注意的是,同时设置时不是用一般的|符号来组合,而是用+。。。。。。这点还真是比较奇怪,难道Mac下的接口都是这样设置的吗?-_-!对了,不需要设置这样的常量感到奇怪,Mac下的代码风格有所不同,以前Windows下(其实我在其他平台也是这样),常量习惯性的全大写,这样大家就能知道这是不能改变的宏或者常量,但是Mac下的接口不是这样。。。。但是,知道它们都是系统常量就好了。

实现回调函数


OSStatus MyHotKeyHandler(EventHandlerCallRef nextHandler,EventRef theEvent,
                                                 void *userData) {
        EventHotKeyID hkCom;
        GetEventParameter(theEvent,kEventParamDirectObject,typeEventHotKeyID,NULL,
                                          sizeof(hkCom),NULL,&hkCom;);
        int l = hkCom.id;
        
        switch (l) {
                case 1: //do something
                        convertRtfToHtml();
                        break;
                case 2: //do something
                        convertTextToHtml();
                        break;
        }
        return noErr;
}

这里通过GetEventParameter来获取需要的信息,然后通过事件的ID来分辨是哪个快捷键按下。整个过程基本就是这样了。

小结

接触Cocoa的时间较短,发现接口设置的一些惯例都不太一样,虽然在GUI世界,消息(或者说事件)驱动是肯定的,但是在Windows的Win32 API及MFC那个层面编写代码的时候(.net不了解,就不知道了),消息的流向是知道的,可控制的,消息响应的时候也常常在OnMessage函数中自己去设置,但是Cocoa中感觉封装的层次更加高一些,我不知道消息从那儿来,又到哪儿去,只能通过回调或者控件的binding,action的链接,类的委托来设置我希望被调用的函数,具体啥时候调用,入口在哪,我都无法控制,这点,可能属于从较为底层跑到较为高层有点不适应。作为C/C++程序员,可能难免带上了一定的刨根问底的性格,甚至一定要回溯到系统加载进程的汇编代码才肯善罢甘休,不然总是觉得没有底,在不能彻底了解一个库的源代码前,甚至都不敢大量的使用,不然效率怎么样,内存怎么控制的,健壮不健壮总是没底,到了Objective C with Cocoa的世界后,还有点不适应。。。。。。。。。。

 

原创文章作者保留版权 转载请注明原作者 并给出链接

write by 九天雁翎(JTianLing) -- www.jtianling.com

阅读全文....

MacOS中的剪贴板操作 With Objective C


MacOS中的剪贴板操作 With Objective C

write by 九天雁翎(JTianLing) -- www.jtianling.com

讨论新闻组及文件

每日废话

     从《Macos中的Mercurial GUI工具Murky试用》一文的访问量基本可以看出,国内使用MacOS 的程序员毕竟还是少数…………就如当年关于Linux的文章没啥人看一样。。。。。
    呵呵,但是读者再少,工作还得继续,学习还得继续,我写我的,没人看就当自言自语了。(其实有人看的时候也常常自言自语-_-!)

前言

     对于常发博客的我,以前很明智的放弃了Windows Live Writer来写博客,而是转向了Google Document,所以现在即使在MacOS平台,对写博客都没有太大影响,唯一有个问题,对于技术博客,难免会发一些代码,WLW有很多代码高亮插件可用,Google Docs却没有,因此,转向Google Docs写博客后,深度依赖自己写的代码高亮工具(见《一键在剪贴板中进行语法高亮的工具发布》,此工具竟然还有人提Win7总是会出行号的Bug,因为我家的 Win7删了,所以没法试了,起码说明除了我还真有人用^^,其实,说实话,实在太好用了,最新的源码连弹出窗口的问题都解决了)当时为了方便,使用了一些平台相关特性,并且是用PyQt完成的,所以现在在MacOS中没得用了。。。。。郁闷啊,首先解决这个问题吧。
     本来还是可以使用Qt的,会省很多事,一些东西也不用学了,在Windows,Linux平台我就这样做了,但是我刚接触MacOS,出于学习的目的,我还是使用Objective C With Cocoa来完成,鉴于国内MacOS玩家本来就不多,Objective C玩家就更少了,对本文不做太高期待了…………
    因为上述原因,本文自然还无法实现语法高亮了,见谅,还好发现XCode中的代码复制过来使用RTF格式的,虽然无法实现高亮(Google Docs不知道以RTF格式读取)缩进格式还在,不幸中的万幸了。

真的开始

     参考2指出,MacOS中粘贴板服务是一个独立的进程,名为/usr/bin/pboard,验证一下:
$ ps -A | grep pboard
   81 ??         0:00.00 /usr/sbin/pboard
  408 ttys000    0:00.00 grep pboard

看来没有错。
先看看基本的使用:
因为没有发现命令行下编程使用MacOS剪贴板的办法,所以再简单的测试也使用窗口程序了,先用XCode建立一个Cocoa程序,我这里命名为MacPasteboardTest,
然后用 Interface Builder拖入一个多行的Text Filed用于显示剪贴板的内容。(Windows下叫clipboard,MacOS下叫Pasteboard。)嫌麻烦,按钮都省了,直接通过菜单绑定action,这样还能使用快捷键:)
另外,为了简化程序,我直接使用自动生成的程序的委托类了,不建立自己的类了

MacPasteboardTestAppDelegate.h:

#import Cocoa.h> <br >
@interface MacPasteboardTestAppDelegate : NSObject {
    NSWindow *window;
    NSTextField *textField;
}

@property (assign) IBOutlet NSWindow *window;
@property (nonatomic, retain) IBOutlet NSTextField *textField;

- (IBAction) cut:(id)sender;
- (IBAction) copy:(id)sender;
- (IBAction) paste:(id)sender;

@end

MacPasteboardTestAppDelegate.m

#import "MacPasteboardTestAppDelegate.h"

@implementation MacPasteboardTestAppDelegate

@synthesize window;
@synthesize textField;

- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
    // Insert code here to initialize your application
}

- (void)writeToPasteboard:(NSPasteboard *)pb withString:string{
    [pb declareTypes:[NSArray arrayWithObject:NSStringPboardType]
               owner:self];
    [pb setString:string forType:NSStringPboardType];
}

- (BOOL)readFromPasteboard:(NSPasteboard *)pb {
    NSArray *types = [pb types];
    if ([types containsObject:NSStringPboardType]) {
        NSString *value = [pb stringForType:NSStringPboardType];
        
        [[self textField] setStringValue:value];
        return YES;
    }
    
    return NO;
}

- (IBAction)cut:(id)sender {
    [self copy:sender];
    [[self textField] setStringValue:@""];
}

- (IBAction)copy:(id)sender {
    NSPasteboard *pb = [NSPasteboard generalPasteboard];
    [self writeToPasteboard:pb withString:[textField stringValue]];
}

- (IBAction)paste:(id)sender {
    NSPasteboard *pb = [NSPasteboard generalPasteboard];
    if( ![self readFromPasteboard:pb] ) {
        NSBeep();
    }
}

@end

    然后,将菜单对应的cut,copy,paste的action都与此委托类的相应action绑定就好了。(类似的话都是对知道的人太多了,对于不知道的人这样说估计还是不懂,推荐不懂的可以去看看某个Hello World教程,那样都会了。)
    这里,最最主要的就是使用NSPasteboard这个系统类了,所有的剪贴板操作都依赖于它。这样实现后,基本与参考2中21章的程序差不多了,此例中因为觉得考虑Selected字符串等情况较为麻烦,所有的操作都是直接针对控件的整个字符串,所以有点不符合操作习惯,需要注意一下,因为现在的主题是剪贴板,这些就算了。

剪贴板内容的类型

     可惜的是原书也仅仅这样点到为止了。我的需要更多,所以我需要进一步的挖掘剪贴板的功能,我的目标是直接复制HTML内容到Google Docs中,并且可以正常识别,因为以前的学习经验,知道这牵涉到剪贴板的内容类型,比如上例中都是NSStringPboardType类型。参考2一共列出了17种各种各样的类型,可惜没有HTML类型,我很惊讶,查了查文档,参考2太老了,MacOS 10.5就有HTML类型了,但是剪贴板的类型字符串在MacOS 10.6有了较大的改变,应该仅仅是从非常量改成常量了,使用应该差不多,为了兼容性,我还是使用MacOS 10.5那种非常量的吧。
文档:
Types for Standard Data (Mac OS X 10.5 and earlier)
The NSPasteboard class uses the following common pasteboard data types.

NSString *NSStringPboardType;
NSString *NSFilenamesPboardType;
NSString *NSPostScriptPboardType;
NSString *NSTIFFPboardType;
NSString *NSRTFPboardType;
NSString *NSTabularTextPboardType;
NSString *NSFontPboardType;
NSString *NSRulerPboardType;
NSString *NSFileContentsPboardType;
NSString *NSColorPboardType;
NSString *NSRTFDPboardType;
NSString *NSHTMLPboardType;
NSString *NSPICTPboardType;
NSString *NSURLPboardType;
NSString *NSPDFPboardType;
NSString *NSVCardPboardType;
NSString *NSFilesPromisePboardType;
NSString *NSMultipleTextSelectionPboardType;

Types for Standard Data (Mac OS X 10.6 and later)
The NSPasteboard class uses the following constants to define UTIs for common pasteboard data types.

NSString *const NSPasteboardTypeString;
NSString *const NSPasteboardTypePDF;
NSString *const NSPasteboardTypeTIFF;
NSString *const NSPasteboardTypePNG;
NSString *const NSPasteboardTypeRTF;
NSString *const NSPasteboardTypeRTFD;
NSString *const NSPasteboardTypeHTML;
NSString *const NSPasteboardTypeTabularText;
NSString *const NSPasteboardTypeFont;
NSString *const NSPasteboardTypeRuler;
NSString *const NSPasteboardTypeColor;
NSString *const NSPasteboardTypeSound;
NSString *const NSPasteboardTypeMultipleTextSelection;
NSString *const NSPasteboardTypeFindPanelSearchOptions;

需要注意的是,NSSring本身就是常量,这里所谓从非常量到常量其实是对该指针值而言的,从NSString *到NSString *const的区别在于,NSString *值的指针可以改变指向(虽然不能改变内容,但是对于系统常量来说还是非常危险啊,这应该算是设计缺陷了,所以MacOS 10.6修改过来了),NSString *const的就是无论内容,指向都不能改了。类似于C++中const *及 const * const的区别。这里展示一下这个危险性(勿学),同时使用HTML类型试试,看能不能达到我想要的与Google Docs兼容的效果。
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
    // Insert code here to initialize your application
    
    // notice!It's ugly but legality.
    NSStringPboardType = NSHTMLPboardType;
}

这样的代码竟然是合法的,你说苹果不改能行吗?

如上图所示,NSStringPboardType类型的值已经变了。。。。。。当然,我就需要这个类型,所以我诡异的这样使用了。(勿效仿)可以看出,就算是苹果公司的框架设计者也会犯一些非常低级的错误的。

看看HTML剪贴板的效果

输入上述内容,注意,上面可是带HTML的了啊:)复制,然后在Google Docs中粘贴,如下:

我是从MacPasteboardTest复制过来的

看到了吗?Google Docs识别出来了,就如同我以前在Windows中用Qt实现的一样,HTML内容的剪贴板复制到Google Docs是直接识别的,而不是作为文字常量给输出,那么,事实上,我可以给大家看看将来我的MacOS的语法高亮软件实现的效果了:)
//
//  MacPasteboardTestAppDelegate.m
//  MacPasteboardTest
//
//  Created by JTianLing on 3/3/10.
//  Copyright 2010 JTianLing. All rights reserved.
//

#import "MacPasteboardTestAppDelegate.h"

@implementation MacPasteboardTestAppDelegate

@synthesize window;
@synthesize textField;

- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
        // Insert code here to initialize your application
        
        // notice!It's ugly but legality.
        NSStringPboardType = NSHTMLPboardType;
}

- (void)writeToPasteboard:(NSPasteboard *)pb withString:string{
        [pb declareTypes:[NSArray arrayWithObject:NSStringPboardType]
                           owner:self];
        [pb setString:string forType:NSStringPboardType];
}

- (BOOL)readFromPasteboard:(NSPasteboard *)pb {
        NSArray *types = [pb types];
        if ([types containsObject:NSStringPboardType]) {
                NSString *value = [pb stringForType:NSStringPboardType];
                
                [[self textField] setStringValue:value];
                return YES;
        }
        
        return NO;
}

- (IBAction)cut:(id)sender {
        [self copy:sender];
        [[self textField] setStringValue:@""];
}

- (IBAction)copy:(id)sender {
        NSPasteboard *pb = [NSPasteboard generalPasteboard];
        [self writeToPasteboard:pb withString:[textField stringValue]];
}

- (IBAction)paste:(id)sender {
        NSPasteboard *pb = [NSPasteboard generalPasteboard];
        if( ![self readFromPasteboard:pb] ) {
                NSBeep();
        }
}

@end

    效果不错吧:)MacOS版本的Code Highlighter已经不远了。。。。。。。呵呵,别流口水啊。

 

不足之处

     做了3个版本的语法高亮软件了,也试用了不少别的Code Highlighter,甚至还有在线版的,其实还是我自己以前做的那个最符合我自己的需求,毕竟,我就是针对Google Docs,我也就只需要HTML格式的剪贴板就可以了。但是,一直以来其实有个地方很不完美,我的语法高亮完全依赖Vim,(当然,依赖也没有什么不好),这是其一,另外,Vim的语法高亮的确是漂亮,但是已经无法复原VS/XCode/Eclipse本身的效果了,但是其实无论从上述哪个IDE中拷贝代码,其实原来的高亮信息其实是在的,只是Google Docs比较蠢(Google Docs is silliness),仅能识别HTML格式的剪贴板内容不能识别其他内容(比如RTF)。
    举个例子,看看XCode本身的语法高亮内容在剪贴板中的显示:(利用了MacOS 10.6中Finder的Show Clipboard功能,啊?怎么到了这里变成clipboard啊?类的命名全叫pasteboard。。。。唉,估计MacOS与XCode的开发者不是同一批。。。。)

其实语法识别内容是比VIM要多的多的。。。。。注意 cilpboard窗口下面的文字,Clipboard contents: rich text(RTF),多么笨的Google Docs啊,连RTF都识别不了。。。。。。。。。。其实,最最好的解决方案应该是为Google Docs开发插件,以直接识别RTF格式,再次一点的,就是制作一个RTF->HTML的软件然后再到Google Docs中了。。。。。有时间我尝试一下吧。

最后

     第一次在Mac Book上写技术博客,这么点的东西花了我一个晚上。。。。。。。。悲哀。。。。。。毕竟对XCode和Objective C还是不太熟悉啊,当然,MacOS下的输入也不算太好,苹果的笔记本键盘嘛。。。。其实我感觉有点硬,达不到运指如飞的境界。。。。呵呵,扯远了。

参考

1.《Objective-C 2.0 程序设计》Stephen G. Kochan著
2.《苹果开发之Cocoa编程》第三版 Aaron Hillegass著
3. 苹果在线文档
    

 

原创文章作者保留版权 转载请注明原作者 并给出链接

write by 九天雁翎(JTianLing) -- www.jtianling.com

阅读全文....

游戏中的动画简介

write by 九天雁翎(JTianLing) -- www.jtianling.com

讨论新闻组及文件

前言

    本文仅仅介绍简单的动画技术而已,主要还是为以后发布关于更丰富的动画表现做基础,所以不要对本文期待太高。全文的技术含量也就比hello world高那么一点点。在简单的介绍了一些动画的概念之后,然后用C++,java applet分别实现了一个简单的动画。至于怎么在网页中运行java applet的问题,可以参考以前的文章《在CSDN博客中部署及运行JAVA Applet》。需要注意的是,要看到java applet自然需要先安装过java,还需要在浏览器中允许才行。


什么是动画

事实上,最简单的解释就是运动的图画-_-!我们研究的是计算机中的动画。
计算机动画动画与运动是分不开的,可以说运动是动画 的本质,动画是运动的艺术。从传统意义上说,动画是一门通过在连续多格的胶片上拍摄一系列单个画面,从而产生动态视觉的技术和艺术,这种视觉是通过将胶片 以一定的速率放映的形式体现出来的。一般说来,动画是一种动态生成一系列相关画面的处理方法,其中的每一幅与前一幅略有不同。
    计算机动画是采用连续播放静止图像的方法产生景物运动的效果,也即使用计算机产生图形、图像运动的技术。计算机动画的原理与传统动画基本相同,只是在传统动画的基础上把计算机技术用于动画的处理和应用,并可以达到传统动画所达不到的效果。由于采用数字处理方式,动画的运动效果、画面色调、纹理、光影效果等 可以不断改变,输出方式也多种多样。” -- 来自参考一

计算机动画的基本原理
    根据运动的控制方式可将计算机动画分为实时(real-time)动画和逐帧动画(frame-by-frame)两种。实时动画是用算法来实现物体的运动。逐帧动画也称为帧动画或关键帧动画,也即通过一帧一帧显示动画的图像序列而实现运动的效果。根据视觉空间的不同,计算机动画又有二维动画与三维动画之分。”-- 还是来自参考一

我们研究的主要是实时动画,其实说简单点就是实时计算出来的动画,而不是原来做好的,现在拿来放,但是,效果上,我们还是一帧一帧的播放,只不过每一帧都是实时计算出来的。每一秒播放的帧数就是常提起的FPS(Frames Per Second),一般对于电脑游戏来说,每秒30帧是底线,60帧是最理想的境界,手机游戏的话20帧也有。要是FPS太低,容易产生跳跃停顿现象,即我常说的“像放幻灯片”(不知道别人常说不)。事实上,在硬件条件的限制内尽可能实现更好的游戏画面,并且保持较高的FPS,这是游戏程序员奋斗不息的永恒主题。


动画在程序中的表现(首先以C++为例)

    事实上,因为动画的原理,用于描绘动画的程序(游戏也算,以下略)与普通程序非常不一样,普通程序往往等待用户输入后才有反应,平时可以空闲,但是动画不行,每一帧都得实实在在的计算,绘制。
    所以,程序的结构也就与普通的程序不太一样,首先用从《Windows游戏编程大师技巧》中的游戏框架中抽出的伪码看看一个动画程序大概要的东西。

// initialize game here
Game_Init();

// enter main event loop
while(TRUE)
{
    // test if there is a message in queue, if so get it
    if (PeekMessage(&msg;,NULL,0,0,PM_REMOVE))
    {
        // test if this is a quit
        if (msg.message == WM_QUIT)
            break;

        // translate any accelerator keys
        TranslateMessage(&msg;);

        // send the message to the window proc
        DispatchMessage(&msg;);
    } // end if

    // main game processing goes here
    Game_Main();

} // end while

// closedown game here
Game_Shutdown();

// return to Windows like this
return(msg.wParam);

int Game_Main(void *parms = NULL, int num_parms = 0)
{
        DWORD dwStartTime;

        dwStartTime = GetTickCount();
        // this is the main loop of the game, do all processing here

        // for now test if user is hitting ESC and send WM_CLOSE
        if (KEYDOWN(VK_ESCAPE))
                SendMessage(ghWnd,WM_CLOSE,0,0);

        // another key down is test here

        SceneShow();

        // control the frame rate
        while(GetTickCount() - dwStartTime < TIME_IN_FRAME)
        {
                Sleep(1);
        }

        // return success or failure or your own return code here
        return(1);

} // end Game_Main

以上就是一个动画程序(其实是个游戏程序)需要的核心要素,主要部分就是运算,显示的循环,并且,需要控制住循环的频率(实际也就控制了显示的FPS),在游戏程序中还需要接受用户的输入。最最主要的是,即使没有任何输入,程序还是在不停的循环,这点与Windows程序的消息驱动有很大的区别,一般来说,将动画的播放与帧相关,也就是绘制时以帧为单位来决定如何绘制,被称为基于帧,同样的还有基于时间的动画,这点用在游戏中也是一样的,其实说起来,游戏本质上就是接受玩家输入的可交互式动画。两种方式在FPS稳定时效果一致,在FPS不稳定时有差别,一般来说,基于帧的更加容易实现,基于时间的游戏可以针对不同的平台使用不同的帧率。

上述框架广泛的被我博客中学习OpenGL部分的代码使用,要看到实际的例子随便下点源代码都可以看到。很多OpenGL的例子都是用了上述框架,并且绘制了动画。这里就不再多举例子了。《 Win32 OpenGL系列专题》中很多例子都是。


java applet动画


import java.applet.Applet;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Image;
import java.util.Date;

import javax.xml.crypto.Data;

public
class appletAnimation extends java.applet.Applet implements Runnable {
    int frame;
    int delay;
    Thread animator;

    /**
     * Initialize the applet and compute the delay between frames.
     */
    public void init() {
    int fps = 30;
    delay = (fps > 0) ? (1000 / fps) : 100;
    }

    /**
     * This method is called when the applet becomes visible on
     * the screen. Create a thread and start it.
     */
    public void start() {
    animator = new Thread(this);
    animator.start();
    }

    /**
     * This method is called by the thread that was created in
     * the start method. It does the main animation.
     */
    public void run() {
        // Remember the starting time
        long tm = System.currentTimeMillis();
        while (Thread.currentThread() == animator) {
            // Display the next frame of animation.
            repaint();

            // Delay depending on how far we are behind.
            try {
                tm += delay;
                Thread.sleep(Math.max(0, tm - System.currentTimeMillis()));
            } catch (InterruptedException e) {
                break;
            }

            // Advance the frame
            frame++;
        }
    }
    
    public void paint(Graphics g) {
        g.setColor(Color.black);
        g.drawString("Frame " + frame, 0, 30);
        }

    /**
     * This method is called when the applet is no longer
     * visible. Set the animator variable to null so that the
     * thread will exit before displaying the next frame.
     */
    public void stop() {
    animator = null;
    }
}

遵照上面的思路,为java applet程序建立循环,FPS控制,显示的更新,OK,也就完成了动画的显示了。唯一与C++中不同的是,驱动的不再是一个死循环,而是一个新的线程,其他的元素完全没有改变。参考二Animation in Java Applets》是很完整的动画的例子。

其他语言的例子:事实上,在我的博客中还有关于OpenGL, Qt, Windows GDI, Small Basic动画的例子,这里我就不列出来了,自己去搜索一下吧,其实理解了动画形成的思想,大同小异.

最后:其实本文本来想讲的更多的,最后拖得太久,所以草草结束了,远没有完成原本想要完成的内容. 并且此时Google Document无法发布,还是转到WLW中发布的,为啥WLW可用,Google Doc突然不可用了?自从CSDN在春节后开放Meta API就是如此,无奈。

参考:
一、《计算机动画基础》http://www.telecarto.com/content/maincontent/multimediadesign/Animation/cha_713.htm -- 关于动画概念讲的很多
二、《Animation in Java Applets》,很详细的java applet教程,从简单的文字动画到稍微复杂点的动画,由浅入深,通俗易懂,例子丰富。

 

 

原创文章作者保留版权 转载请注明原作者 并给出链接

write by 九天雁翎(JTianLing) -- www.jtianling.com

阅读全文....

Qt/PyQt中使用系统全局的快捷键

write by 九天雁翎(JTianLing) -- www.jtianling.com

讨论新闻组及文件

估计这种小的知识会有几篇文章,除了全局快捷键部分外,其他的都比较简单,都是我实现onekeycodehighlighter"中碰到的一些小问题,这里顺便整理一下。事实上,稍微懂一点的人,去看看one key code highlighter的源代码都能明白了。这里相当于将其详细的剖析一下。。。。。。。另外,实现上用Python+PyQt,事实上,主要的部分是对Qt的一些类的使用,所以其实看懂了C++的Qt中使用上是一样的。啥?你看不懂Python?好的,这就是我为什么靠C++吃饭,却学习JAVA,JavaScript,Lua,Python,Bash的一个原因,不然你看不懂别人在写啥-_-!(当然,我基本上也就学到能看懂)
对于pyQt完全不懂的,这里也不用看了,《pyqt的学习(1) 入门》,《pyqt(2) 对话框...》可以看看,但是写的有点乱,因为那时候我懂得也少(不代表现在就懂的多了)

概述

使用系统全局的快捷键总是键很酷的事情,在你的程序已经失去焦点的时候,还能响应用户的按键,完成任务,多酷啊。特别的,我的one key code highlighter只有一个SystemTrayIcon(托盘图标)和一些菜单,不使用系统全局的快捷键而用菜单去实现命令的话,我还不如用快捷方式去使用chc2c呢(就如我以前做的那样)。所以,对于这个我自己需要而实现的软件,怎么说,也需要有这个功能,无奈的是,偏偏Qt中其实原生不支持此功能,那么,只好放弃移植性去使用win32 API了。

Qt中的实现

主要的API为:(来自MSDN)


BOOL RegisterHotKey(          HWND hWnd,
    int id,
    UINT fsModifiers,
    UINT vk
);

在Qt中实现并不算什么大问题,《Qt中使用全局热键》中有一些说明,此文说明了一件事情,技术文章的价值不以篇幅来衡量,而是以技术含量衡量,此文没有太多的文字,但是详细解释了所有过程。
主要步骤为用RegisterHotKey向系统注册全局的快捷键,然后重载QApplication的winEventFilter函数,并响应msg为WM_HOTKEY时的消息,整个过程类似于在Windows下的OnMessage进行消息响应,只是这次是在Qt中。原文给出的示例代码如下:(原文排版有点乱,但是不损其技术价值)


bool winEventFilter(MSG *msg, long *result)
{
    if(WM_HOTKEY==msg->message)
    {
        qDebug()<<"winmsg return true";
        emit hotKey(int(msg->wParam),LOWORD(msg->lParam),HIWORD(msg->lParam));
        return TRUE;
    }
    return FALSE;
}
这种实现很简单,我就不多说了,Qt嘛,毕竟还是C++,只不过这里放弃移植性调用Win32 API,并且响应消息而已,没有什么太多新的东西。

PyQt中的实现

相对于Qt中的实现来说,对于Qt中没有的东西,很显然PyQt中也很难有了,那么,我们还是只能通过调用Win32 API了,而在Python中调用Win32 API就没有那么太简单了。。。。。。。
介绍的是用ctypes来调用,当然,因为感觉此部分会与本主题太偏,所以额外写了一篇文章讲述。《Python与C之间的相互调用(Python C API及Python ctypes库)》,看了前文就会知道,其实用Python C API包装以下RegisterHotKey也可以实现一样的效果,知识用ctypes更简单一些。
这里就直接讲RegisterHotKey的调用了。以下是一些代码片段。。。。。


from ctypes import c_bool, c_int, WINFUNCTYPE, windll
from ctypes.wintypes import UINT



prototype = WINFUNCTYPE(c_bool, c_int, c_int, UINT, UINT)
paramflags = (1, 'hWnd', 0), (1, 'id', 0), (1, 'fsModifiers', 0), (1, 'vk', 0)
self.RegisterHotKey = prototype(('RegisterHotKey', windll.user32), paramflags)



r = self.RegisterHotKey(c_int(self.mainWindow.winId()), HOT_KEY_ID, config.modifier, ord( config.hotkey.upper() ))
if not r:
      QtGui.QMessageBox.critical(None, 'Hot key', 'Can't Register Hotkey Win + Z')
      sys.exit(1)
这里用了比直接调用RegisterHotKey更复杂的方法来使用ctypes,(在Python与C之间的相互调用(Python C API及Python ctypes库)》中描述了最简单的办法),好处是实现了“命名参数”及参数默认值,这里虽然实际没有使用-_-!另外,利用config中的配置的大写字母的ord,来表示Windows中的虚拟键值真是很方便,为什么这样能省去那一大堆的VK_*定义?因为WinUser.h中就是这样定义这些VK_*的。。。。。。。
还有,mainWindow实际是一个QMainWindow的对象,其winId函数可以获取到Windows窗口的句柄,这里将其转化为c_int而不是HWND,因为在Python中不允许从int到HWND的转换(这有点扭曲),知道原因的请告诉我。完成了这些后,QApplication的winEventFilter函数的重载还是少不了的。

以下是我的一段实现代码:


    # these code don't have compatibility with other OS
    def winEventFilter(self, msg):
        debug_out("Message: " + str(hex(msg.message)) )
        if msg.message == WM_HOTKEY:
            if isClipboardEmpty():
                self.mainWindow.trayIcon.showMessage("failed", "Clipboard is Empty.")
                return True, 0
            debug_out("Got the Hotkey!")
            debug_out(config.filename)
            
            import imp
            imp.reload(config)
            chc2c(self.mainWindow.syn, config.color, config.isLineNumber, config.filename)
            self.mainWindow.trayIcon.showMessage("success", "transformed the data in clipboard to html.")
            return True, 0
        
        return False, 0

基本思路与C++中并无差异,查找到WM_HOTKEY,然后响应之。我这里利用了reload来达到每次都动态查询配置的效果^^chc2c就是我的主要函数。
以上就是PyQt中实现全局快捷键的全部过程了。

另外,我本来以为假如愿意使用PyWin32的话,直接可以调用其中的RegisterHotKey,后来竟然在里面没有找到,奇了怪了。

参考文章

Qt中使用全局热键

完整源代码获取说明

由于篇幅限制,本文一般仅贴出代码的主要关心的部分,代码带工程(或者makefile)完整版(如果有的话)都能用Mercurial在Google Code中下载。文章以博文发表的日期分目录存放,请直接使用Mercurial克隆下库:

https://onekeycodehighlighter.googlecode.com/hg/

Mercurial使用方法见《分布式的,新一代版本控制系统Mercurial的介绍及简要入门

要是仅仅想浏览全部代码也可以直接到google code上去看,在下面的地址:

http://code.google.com/p/onekeycodehighlighter/source/browse/

 

原创文章作者保留版权 转载请注明原作者 并给出链接

write by 九天雁翎(JTianLing) -- www.jtianling.com

阅读全文....

Qt/PyQt的系统托盘图标(SystemTrayIcon)实现

write by 九天雁翎(JTianLing) -- www.jtianling.com

讨论新闻组及文件

估计这种小的知识会有几篇文章,除了全局快捷键部分外,其他的都比较简单,都是我实现“onekeycodehighlighter"

中碰到的一些小问题,这里顺便整理一下。事实上,稍微懂一点的人,去看看one key code highlighter的源代码都能明白了。这里相当于将其详细的剖析一下。。。。。。。另外,实现上用Python+PyQt,事实上,主要的部分是对Qt的一些类的使用,所以其实看懂了C++的Qt中使用上是一样的。啥?你看不懂Python?好的,这就是我为什么靠C++吃饭,却学习JAVA,JavaScript,Lua,Python,Bash的一个原因,不然你看不懂别人在写啥-_-!(当然,我基本上也就学到能看懂)
对于pyQt完全不懂的,这里也不用看了,《pyqt
的学习(1) 入门


》,《pyqt
(2) 对话框...


》可以看看,但是写的有点乱,因为那时候我懂得也少(不代表现在就懂的多了)

SystemTrayIcon在官方的Demo中有一个示例,(那些示例有些可真酷啊!)位置在Desktop->System Tray中。该示例展示了大部分需要用到的内容。包括实现TrayIcon,显示气泡信息,TrayIcon的菜单等等。

创建系统系统托盘图标(TrayIcon)

主要用到的类是QtGui.QSystemTrayIcon。
图标用QtGui.QIcon类来表示,可以以文件名字符串为构造函数的参数。如:

icon = QtGui.QIcon('jt.png
')

然后用QtGui.QSystemTrayIcon的


setIcon(icon) 去完成系统TrayIcon的创建。OK,已经完成80%了,图标已经出来了。

气泡信息

再然后呢?希望有特定的气泡信息?

QtGui.QSystemTrayIcon的showMessage
可以完成。

菜单

再然后呢?希望有菜单?这个稍微复杂点,在Qt中,菜单是一个一个的Action,如下建立Action:


        self.quitAction = QtGui.QAction("&Quit
", self,
                                        triggered=QtGui.qApp.quit)
        
        self.aboutAction = QtGui.QAction("&About
", self,
                                        triggered=self.about)
注意上述Action的triggerd参数,实际上是一个Callable的回调函数,意思是点击此菜单时进行的操作。
然后将Action添加进某个Menu



self.trayIconMenu = QtGui.QMenu(self)

self.trayIconMenu.addAction(self.aboutAction)
self.trayIconMenu.addAction(self.quitAction)
然后将Menu

关联上TrayIcon
self.trayIcon.setContextMenu(self.trayIconMenu)

完成了。

 

CheckBox菜单

 

我的需求更加复杂一点,希望有可以Check的菜单,当然,这个需求已经超出SystemTrayIcon相关的需求了,与Qt中的菜单有关。

将需要实现成Check菜单的所有命令添加到一个Action组中,在Qt中称为QActionGroup。



        self.synGroup = QtGui.QActionGroup(self)
        
        for

 syn in

 config.syntaxSupport:
            action = QtGui.QAction(syn, self, checkable=True,
                triggered=self.setSyn)
            self.synGroup.addAction(action)

 

需要注意的是,每个Action的checkable参数设为True,表示是CheckBox类型的菜单。
通过某个Action的setChecked来选中,比如:

        actions = self.synGroup.actions()


        if

 len(actions) != 0:
            actions[0].setChecked(True)

主要注意的是,在Qt中QActionGroup返回的是一个QList的列表,但是在PyQt已经将其转换为Python中原生的list了,这样更加符合Python的使用习惯,当然,调用方法的时候也需要注意一下了,接口可是不同的,感谢RiverBank(PyQt的创造维护公司)伟大的工作,对于可怜的RiverBank我其实还有话要说,以后再详述吧。
具体哪个菜单选项被Check了,通过


checkedAction = self.synGroup.checkedAction()
来查询,返回的是被Check的Action,此时假如你是通过Action的字符串来查询的话,(比如我)那么调用Action的text

函数获取。

一切都结束了。。。。。。。需要注意几个特别的地方:
1。官方的教程中,有
QtGui.QApplication.setQuitOnLastWindowClosed(False)
这样一句,大概的意思是在应用程序所有窗口都关闭的时候不关闭应用程序,事实上,就我试验,无论是设为True,还是False,都不管关。。。。。。原因不明。
2。在应用程序利用QtGui.qApp.quit关闭后,TrayIcon其实还不会自动消失,直到你的鼠标移动到上面去后,才会消失,这是个问题,(如同你terminate一些带TrayIcon的应用程序时出现的状况),这种问题的解决我是通过在程序退出前将其setVisible(False)来完成的。

 

 

 

 

完整源代码获取说明

由于篇幅限制,本文一般仅贴出代码的主要关心的部分,代码带工程(或者makefile)完整版(如果有的话)都能用Mercurial在Google Code中下载。文章以博文发表的日期分目录存放,请直接使用Mercurial克隆下库:

https://onekeycodehighlighter.googlecode.com/hg/

Mercurial使用方法见《分布式的,新一代版本控制系统Mercurial的介绍及简要入门

要是仅仅想浏览全部代码也可以直接到google code上去看,在下面的地址:

http://code.google.com/p/onekeycodehighlighter/source/browse/

 

原创文章作者保留版权 转载请注明原作者 并给出链接

write by 九天雁翎(JTianLing) -- www.jtianling.com

 

阅读全文....

Qt/PyQt中操作系统剪贴板(clipboard)

write by 九天雁翎(JTianLing) -- www.jtianling.com

讨论新闻组及文件

估计这种小的知识会有几篇文章,除了全局快捷键部分外,其他的都比较简单,都是我实现onekeycodehighlighter"中碰到的一些小问题,这里顺便整理一下。事实上,稍微懂一点的人,去看看one key code highlighter的源代码都能明白了。这里相当于将其详细的剖析一下。。。。。。。另外,实现上用Python+PyQt,事实上,主要的部分是对Qt的一些类的使用,所以其实看懂了C++的Qt中使用上是一样的。啥?你看不懂Python?好的,这就是我为什么靠C++吃饭,却学习JAVA,JavaScript,Lua,Python,Bash的一个原因,不然你看不懂别人在写啥-_-!(当然,我基本上也就学到能看懂)
对于pyQt完全不懂的,这里也不用看了,《pyqt的学习(1) 入门》,《pyqt(2) 对话框...》可以看看,但是写的有点乱,因为那时候我懂得也少(不代表现在就懂的多了)

概述

系统剪贴板的操作在Qt中有原生的支持,这点很强大,操作起来比Windows本身附带的要简单的多,更重要的是,这是跨平台的。
Windows中的剪贴板其实很简单,只有一种,就是你选择然后CTRL-C的那一个。在Vim中用寄存器"+"存储,(不懂就算了)。Linux中剪贴板有两种,一种是鼠标选中后立刻就生效的,在Vim中用寄存器"+"存储,称作select剪贴板,另外一种就是类似Windows下的那种剪贴板,vim中用寄存器"*"存储。
剪贴板作为应用程序中较为通用的一种共享数据的方式,应用较为广泛,就我所知,这好像是唯一一种用户可以很方便操作的应用程序共享数据的途径,其他的如Socket等,编程的时候是很容易实现,用户要操作就难了。

操作

简单的剪贴板操作,很简单,Qt中用QClipboard类来表示
在Qt中用
 clipboard = QtGui.QApplication.clipboard()
获取到剪贴版的对象,然后用text表示获取到文本数据,(类似CTRL-P),用setText来设置文本数据。(类似CTRL-C)

更深入的操作

对于普通的文本操作,这两个函数就足够了。以前我也是这样做的。但是,我发现一个现象,那就是复制网页上的数据后,在Google Document上paste的时候,是直接可以复原原来的网页内容的(虽然常常有些偏差),但是我转换后的HTML源码是用setText设置到剪贴板中的话,paste出来的就是源码,说明肯定里面还有蹊跷,要是我的转换工具,直接粘贴就可以在Google Document中出现语法高亮过的文字多好啊,于是我查看了一下QClipboard类,及MSDN。果然,在剪贴板中保存的不仅仅是文字,还可以是一些有格式的内容,在windows中可以保存OLE的东西。。。。Qt中将其统一划分为MimeData。
看看QMimedata这个类就会很惊喜,包括了HTML,Image等很多的东西,当然我要的就是HTML。
于是乎,我通过
mimeData = QtCore.QMimeData()
mimeData.setHtml(clipboard.text())
clipboard.setMimeData(mimeData)

来设置一个转换过的HTML源码,此时就能直接在Google Document上通过粘贴来得到高亮过的代码了:)
但是,在语法文本源代码的地方此时的粘贴就无效了,因为已经没有文本了,经过试验,Qt中不同的数据时相互不影响的,于是再改了一下:


def setClipboardMimeToHTML():
    clipboard = QtGui.QApplication.clipboard()
    mimeData = QtCore.QMimeData()
    mimeData.setText(clipboard.text())
    mimeData.setHtml(clipboard.text())
    clipboard.setMimeData(mimeData)

哈哈,能够粘贴HTML的地方,显示的就是HTML,只能显示文本的地方,粘贴的即是HTML的源码。好不强大,这也就是最后,你们在onekeycodehighlighter" 中实际使用的效果。
总之,我是对自己做的这个工具很满意了:)

完整源代码获取说明

由于篇幅限制,本文一般仅贴出代码的主要关心的部分,代码带工程(或者makefile)完整版(如果有的话)都能用Mercurial在Google Code中下载。文章以博文发表的日期分目录存放,请直接使用Mercurial克隆下库:

https://onekeycodehighlighter.googlecode.com/hg/

Mercurial使用方法见《分布式的,新一代版本控制系统Mercurial的介绍及简要入门

要是仅仅想浏览全部代码也可以直接到google code上去看,在下面的地址:

http://code.google.com/p/onekeycodehighlighter/source/browse/

 

原创文章作者保留版权 转载请注明原作者 并给出链接

write by 九天雁翎(JTianLing) -- www.jtianling.com

阅读全文....

Python与C之间的相互调用(Python C API及Python ctypes库)

write by 九天雁翎(JTianLing) -- www.jtianling.com

讨论新闻组及文件

我实现onekeycodehighlighter
"


中碰到的一些小问题,需要实现全局快捷键,但是是事实上Qt并没有对全局快捷键提供支持,那么用Qt的话就只能通过Win32Api来完成了,而我,用的是PyQt,还需要用Python来调用win32 API,事实上,都没有什么难的。

因为Python如此的流行,导致,开源社区按照自己的爱好,对于Python与C之间互相调用上,各自开发了自己想要的调用方式,其中包括用Python C API来完成,包括ctypes这个Python标准库,还有那一大堆的各式各样的绑定方案如SIP,Boost::Python等,要知道,Python流行到什么程序,Boost库号称C++准标准库,唯一对C++以外的一种语言提供了支持,那就是Python,Python还是Symbian除C++,JAVA外支持的第3种语言,当年在原来的公司,我还一直以为Python是个新鲜的小玩意儿,要我鼓捣Python C API的时候很新鲜,(事实上原公司的确没有用Python的人)到了新公司一看,啊~~~公司只允许使用3中语言,C++,JAVA,还有Python,而大家对Python那都是驾轻就熟,信手拈来,常用来开发一些工具及脚本,呵呵,世界原来与我想象的并不同。
这里将以前工作中用到的Python C API知识,及最近用到的ctypes库的知识梳理一下。

Python C API

此部分可以参考我原来的文章《python
c
api
使用心得...


》,这里只是会有一些实际的例子,原来那是一个大概流程的描述。
某年某月,在我开始学习Python古老的岁月中(我不是倚老卖老啊)。。。。ctypes还不存在,那时候我们都是老实的用C语言,调用Python C
API来完成从Python中调用C语言函数的任务,我学习Python的时候还在想,哈哈哈哈哈,我以前学过C/C++,我可以很熟练的调用
Python C API来完成Python调用Win32 API这样的任务,我多了不起啊:)这个时候的感觉就像,嘿,Python你不是了不起吗。。。。还不是没有办法逃离C语言的魔掌。。。。此时,画面中出现的是K&R嘿嘿嘿嘿的冷笑。。。。Guido van Rossum在他们脚下抱着头哭了。。。。。。。
那时候,情况大概是这样的:

准备工作:

闲话少说,看看Python C API。事实上,Python C API比起Lua的API了来说,清晰了很多,这也符合Pythonic的风格,就算这时Python C API是设计给C语言使用者使用的,还是这样的风格,比起Lua API那种汇编式的接口,(据说为了效率,可以直接操作每个数据)强了太多了。
要使用Python C API,用普通的二进制包是不行的,得下源码包。这里我用3.1.1的源码包为例:Source Distribution

Python的源码在Windows的版本中已经完全换到VS2008了,直接用VS2008打开在PCbuild目录下的工程即可,对于VS2005及以前的用户打开PC目录下的其他版本工程。我们编译debug版本的pythoncore会得到python31_d.lib,python31_d.dll两个文件,需要的头文件在Include目录下,还需要将pyconfig.h文件从PCBuild目录下拷贝到Include中,(硬要直接指定也可以)这样准备工作就已经齐了。

Python C API有两个方向的使用方式,从C中调用Python脚本及利用C扩展Python。
先讲简单的从C中调用Python,也就是常说的在C中内嵌Python。

C中内嵌Python

新建立一个工程,首先需要将工作目录设置到Python-3.1.1PCbuild中,以获取到动态库,至于静态库的包含,Include目录的指定,那自然也是少不了的。文件中需要包含Python.h文件,这也是必须的。
接口中
    Py_Initialize();
    Py_Finalize();
一对的调用是必须的,一个用于初始化Python的动态库,一个用于释放。释放时会输出[31818 refs],意义不明。

PyRun_SimpleString

可用于执行简单的Python语句。如下:



#include
"python.h"

int

 main(int

 argc, char

* argv[])
{
    Py_Initialize();

    PyRun_SimpleString("print(
"
Hello World
"
)"
);
    Py_Finalize();

    system("PAUSE"
);
    return

 0
;
}

 

此时,输出为:

Hello World
[31829 refs]
请按任意键继续. . .

 

此时可以执行一些Python语句了,并且,特别需要注意的是,在一个Py_Initialize();与Py_Finalize();之间,Python语句执行是在同一个执行环境中,不懂什么意思?看个示例就知道了。



int

 main(int

 argc, char

* argv[])
{
    Py_Initialize();

    PyRun_SimpleString("str =
"
Hello World
"
"
);
    PyRun_SimpleString("print(str)"
);

    Py_Finalize();

    system("PAUSE"
);
    return

 0
;
}

此例与上例输出是一样的,懂我的意思了吧?意思就是以前执行的语句对后面的语句是有效的,相当于在同一个交互式命令行中顺序执行语句。

获取返回值

PyRun_SimpleString有的缺点,文档中的描述是:

Returns 0
on success or
-1
if an exception was
raised.

那么你就无法在Python及C语言中传递任何信息。我们需要高级点的函数才行。

 

PyObject* PyRun_String(const char *str, int start, PyObject *globals, PyObject *locals)
就是干这个的。
但是需要注意的是此函数的一些参数的获取,按照想当然的给他们置空可是不行的,如下例所示:


#include
"python.h"

int

 main(int

 argc, char

* argv[])
{
    Py_Initialize();

    PyRun_SimpleString("x = 10"
);
    PyRun_SimpleString("y = 20"
);
    PyObject* mainModule = PyImport_ImportModule("__main__"
);
    PyObject* dict = PyModule_GetDict(mainModule);
    PyObject* resultObject = PyRun_String("x + y"
, Py_eval_input, dict, dict);

    if

(resultObject)
    {
        long

 result = PyLong_AsLong(resultObject);
        printf("
%d
"
, result);
        Py_DECREF(resultObject);
    }

    Py_Finalize();

    system("PAUSE"
);
    return

 0
;
}
这里我利用了一个知识,那就是
PyRun_SimpleString实际是将所有的代码都放在
__main__

模块中运行,注意啊,没有导入正确的模块及其dict,你会运行失败,失败的很惨。至此,C语言已经于Python来了个交互了。
呵呵,突然觉得深入下去就没有尽头了。。。。。。。还是点到为止吧。
稍微深入点的可以去看《Programming Python》一书。在啄木鸟
上有此书及一些译文。Part VI: Integration 部分Chapter 23. Embedding Python,有相关的知识。

利用C扩展Python

此部分在《Programming Python》的Chapter 22. Extending Python 部分有介绍。
这里也只能开个头了,最多告诉你,其实,这些都没有什么难的。稍微复杂点的情况《python
c
api
使用心得...


》一文中有介绍。
配置上与前面讲的类似,一般来说,利用C扩展Python最后会生成一个动态库,不过这个动态库的后缀会设为.pyd,只有这样,import的时候才会自动的查询到。
另外,为Python写扩展要遵循Python的那套规则,固定的几个命名。
首先看自带的例子:


#include
"Python.h"

static

 PyObject *
ex_foo(PyObject *self, PyObject *args)
{
    printf("Hello, world
n
"
);
    Py_INCREF(Py_None);
    return

 Py_None;
}

static

 PyMethodDef example_methods[] = {
    {"foo"
, ex_foo, METH_VARARGS, "foo() doc string"
},
    {NULL
, NULL
}
};

static

 struct

 PyModuleDef examplemodule = {
    PyModuleDef_HEAD_INIT,
    "example"
,
    "example module doc string"
,
    -1
,
    example_methods,
    NULL
,
    NULL
,
    NULL
,
    NULL

};

PyMODINIT_FUNC
PyInit_example(void

)
{
    return

 PyModule_Create(&examplemodule);
}

这个例子包含了全部C语言为Python写扩展时的基本信息:
1.PyInit_example是最后的出口,其中需要注意的是example不仅仅代表example的意思,还代表了最后生成的库会用example命名,也就是你调用此库会需要使用
import example


的形式。
2.static

 struct

 PyModuleDef examplemodule的存在也是必须的,指定了整个模块的信息,比如上面

的"example module doc string", 模块的说明文字。每个参数的含义上面已经有些演示了。
全部内容可以参考文档中关于PyModuleDef的说明
3.example_methods是一个函数列表,事实上表示此模块中含有的函数。此例中仅含有
foo一个函数。
static

 PyObject *
ex_foo(PyObject *self, PyObject *args)
{
    printf("Hello, world
n
"
);
    Py_INCREF(Py_None);
    return

 Py_None;
}


就是整个函数的具体实现了,此函数表示输出"Hello, world",还是hello world。。。。。。。。这个world还真忙啊。。。。天天有人say hello。

这个Python本身附带的例子有点太简单了,我给出一个稍微复杂点的例子,还是我最喜欢的MessageBox,最后的效果自然还是Hello world。。。。。。。。。。。

#include

static

 PyObject *
MessageBox(PyObject *self, PyObject *args)
{
    LPCSTR lpText;
    LPCSTR lpCaption;
    UINT uType;

    PyArg_ParseTuple(args, "ssi"
, &lpText, &lpCaption, &uType);

    int

 result = MessageBoxA(0
, lpText, lpCaption, uType);

    PyObject* resultObject = Py_BuildValue("
%i
"
, result);

    return

 resultObject;
}

static

 PyMethodDef c_methods[] = {
    {"MessageBox"
, MessageBox, METH_VARARGS, "MessageBox() "
},
    {NULL
, NULL
}
};

static

 struct

 PyModuleDef win32module = {
    PyModuleDef_HEAD_INIT,
    "Win32API"
,
    "Win32 API MessageBox"
,
    -1
,
    c_methods,
    NULL
,
    NULL
,
    NULL
,
    NULL

};

PyMODINIT_FUNC
PyInit_Win32API(void

)
{
    return

 PyModule_Create(&win32module);
}


需要注意的还是需要注意,唯一有点区别的是这里我有从Python中传进来的参数及从C中传出去的返回值了。
PyArg_ParseTuple
用于解析参数
Py_BuildValue 用于构建一个Python的值返回
他们的构建和解析形式有点类似于sprintf等C常见的形式,可是每个字符代表的东西不一定一样,需要注意,文档中比较详细,此例中展示的是String及int的转换。

以生成动态库的方式编译此文件后,并指定为Win32API.pyd文件,然后将其拷贝到Python_d所在的目录(用Python3.1.1源代码生成的调试版本Python),此时import会首先查找*_d.pyd形式的动态库,不然只会搜索release版。
首先看看库的信息:

>>> import Win32API
[44692 refs]
>>> dir(Win32API)
['MessageBox', '__doc__', '__file__', '__name__', '__package__']
[44705 refs]
>>> help(Win32API)
Help on module Win32API:

NAME
    Win32API - Win32 API MessageBox

FILE
    d:python-3.1.1pcbuildwin32api_d.pyd

FUNCTIONS
    MessageBox(...)
        MessageBox()

[68311 refs]
注意到文档的作用了吧?还注意到dir的强大。。。。。。。。。。。。。此时MessageBox已经在Win32API中了,直接调用吧。我这里忽略了窗口的句柄,需要注意。

多么繁忙的World啊。。。。。。。。
此时你会想,太强大了,我要将整个的Win32 API到处,于是Python就能像C/C++语言一样完全操作整个操作系统了,并且,这还是动态的!!!!
没错,不过多大的工作量啊。。。。。。不过,Python这么流行,总是有人做这样的事情的,于是PyWindows出世了。去安装一个,于是你什么都有了。
>>> import win32api
>>> win32api.MessageBox(0, "Great", "Hello World", 0)
1
这样,就能达到上面全部的效果。。。。。。。。。。。

Python ctypes

如此这般,原来Python还是离不开C啊(虽然Python本身使用C写的)。。。,直到。。。。某年某月ctypes横空出世了,于是,完全不懂C语言的人,也可以直接用Python来完成这样的工作了。毫无疑问,Python越来越自成体系了,他们的目标是,没有其他语言!-_-!在Python v3.1.1的文档中如此描述,
ctypes
— A foreign
function library for Python
然后:It can be used to wrap these libraries in pure Python.
注意,他们要的是Pure Python!(我不是想要挑起语言战争。。。。。)
Guido van Rossum开始说,wrap these,in pure Python。。。。不要再用foreign语言,血统不pure的家伙了。

闲话少说,看看ctypes,因为是pure Python嘛,所以看起来很简单,事实上文档也比较详细(当然,还是遗漏了一些细节),下面都以Windows中的Python3.1.1的操作为例:
>>> import ctypes
>>> from ctypes import *
>>> dir(ctypes)
['ARRAY', 'ArgumentError', 'Array', 'BigEndianStructure', 'CDLL', 'CFUNCTYPE', '
DEFAULT_MODE', 'DllCanUnloadNow', 'DllGetClassObject', 'FormatError', 'GetLastEr
ror', 'HRESULT', 'LibraryLoader', 'LittleEndianStructure', 'OleDLL', 'POINTER',
'PYFUNCTYPE', 'PyDLL', 'RTLD_GLOBAL', 'RTLD_LOCAL', 'SetPointerType', 'Structure
', 'Union', 'WINFUNCTYPE', 'WinDLL', 'WinError', '_CFuncPtr', '_FUNCFLAG_CDECL',
 '_FUNCFLAG_PYTHONAPI', '_FUNCFLAG_STDCALL', '_FUNCFLAG_USE_ERRNO', '_FUNCFLAG_U
SE_LASTERROR', '_Pointer', '_SimpleCData', '__builtins__', '__doc__', '__file__'
, '__name__', '__package__', '__path__', '__version__', '_c_functype_cache', '_c
alcsize', '_cast', '_cast_addr', '_check_HRESULT', '_check_size', '_ctypes_versi
on', '_dlopen', '_endian', '_memmove_addr', '_memset_addr', '_os', '_pointer_typ
e_cache', '_string_at', '_string_at_addr', '_sys', '_win_functype_cache', '_wstr
ing_at', '_wstring_at_addr', 'addressof', 'alignment', 'byref', 'c_bool', 'c_buf
fer', 'c_byte', 'c_char', 'c_char_p', 'c_double', 'c_float', 'c_int', 'c_int16',
 'c_int32', 'c_int64', 'c_int8', 'c_long', 'c_longdouble', 'c_longlong', 'c_shor
t', 'c_size_t', 'c_ubyte', 'c_uint', 'c_uint16', 'c_uint32', 'c_uint64', 'c_uint
8', 'c_ulong', 'c_ulonglong', 'c_ushort', 'c_void_p', 'c_voidp', 'c_wchar', 'c_w
char_p', 'cast', 'cdll', 'create_string_buffer', 'create_unicode_buffer', 'get_e
rrno', 'get_last_error', 'memmove', 'memset', 'oledll', 'pointer', 'py_object',
'pydll', 'pythonapi', 'resize', 'set_conversion_mode', 'set_errno', 'set_last_er
ror', 'sizeof', 'string_at', 'windll', 'wstring_at']

一个这样的小玩意儿包含的东西还真不少啊,可以看到主要包括一些C语言的类型定义。
当你import ctypes的时候,一些动态库已经载入了:
>>> print(windll.kernel32)

>>> print(windll.user32)

>>> print(windll.msvcrt)

直接来使用试试吧,我们最喜欢的自然是Hello World。这里直接调用MessageBox。查查MSDN,MessageBox在User32中,我们调用它。
>>> MessageBox = windll.user32.MessageBoxW
>>> MessageBox(0,"Great","Hello World", 0)
然后,就调用了MessageBox了。。。。。。。。

怎么?晕了?比较一下ctypes库及Python C API吧。。。。于是,K&R哭了。。。。。。。。。。。。。
故事以下图开始

以下图结束:

 

原创文章作者保留版权 转载请注明原作者 并给出链接

write by 九天雁翎(JTianLing) -- www.jtianling.com

阅读全文....

一键在剪贴板中进行语法高亮的工具发布

write by 九天雁翎(JTianLing) -- www.jtianling.com

讨论新闻组及文件

我一直希望有个工具能够便捷的进行语法高亮,因为很多地方都可以用到。特别是,假如我在Google Document或者Office中编辑文件的时候,或者是在Windows Live Writer中编写博客文章的时候(WLW中有插件支持,但还是没有此工具这么方便,并且现在写博客的时候我已经改用Google Document了),我都希望能便捷的进行语法高亮,但是并不是都那么容易实现。特别是像Google Document这样不支持插件的工具,因为Google Document不支持插件,不能进行语法高亮,所以长时间以来我都不将其作为博客的编写工具,直到,你知道的,我决定写个工具来解决这个问题。于是,就有了今天这个工具。

其实本程序实际是原来的chc2c工具的一个UI版本,code-highlight-clipboard2clipboard chc2c是一个命令行工具,可能很多人会比较排斥,虽然我建一些快捷方式也能实现比较便捷的效果。当然,怎么说还是UI工具来的方便,此功能托管在Google Code上:onekeycodehighlighter 已经有下载了:ClipboardHighlighter0.1.rar

因为还是使用Gvim来完成实际工作,所以,gvim的安装还是不可少,假如有人发现绿色版简易版支持语法高亮和ToHtml的vim请推荐给我,我直接放在下载包中,这样大家就可以不用安装gvim了。对了,安装后,将gvim添加到PATH中,这样我才能找到并执行它。

可以通过config.py文件来配置,配置文件中的注释说明的很详细了,简单的修改应该没有问题,config文件本身就是一个python脚本,只要你满足我原来的变量名不变,你可以做很多事情。
默认情况下,我仅添加了c,cpp,python,java,javascript的语法高亮支持,其他的语法在config中配置吧。config中除了syntaxSupport 以外,都支持动态改变。比如保存的文件名,是否有行号,颜色主题等。

使用中软件为一个任务栏中的图标,右键点击会出现菜单:

选中的一栏表示使用的语法高亮语言。使用中将需要进行语法高亮的文字用CTRL-C复制到剪贴板中,然后按Win+Z完成转换,然后就可以直接粘贴到任何支持HTML的地方了。可以是网站的编辑器,可以是Windows live writer,office word,Google Document。。。。。。。。。。你也可以在config.py中配置你想要保存的文件名,直接保存成一个HTML文件。

实际编写的主要工具为PyQt。因为此工具有一些特殊的功能要求,比如全局快捷键,比如用ctypes来调用了Win32的API,比如我要使用System Tray Icon,比如对剪贴板的操作等等等等,我觉得可以将这些例子分为单独的文章来讲解,目前仅贴出一些此工具转换的效果给大家看看。
下面是一些示例:
配置文件(Python):


MOD_ALT = 0x0001
MOD_NONE = 0x000
MOD_CONTROL = 0x0002
MOD_SHIFT = 0x0004
MOD_WIN = 0x0008

# the syntax support you want in the trayicon menu
syntaxSupport = ["c", "cpp", "python", "java", "javascript"]

# the global hotkey define,the modifier can be used is listed above.
modifier = MOD_WIN
hotkey = 'Z'

# do you need display line number before every line?
isLineNumber = False

#------------------------------------------------------------------------------------
# you need not change these below at most time if you don't know what it is.
# the color theme you want use. (corresponding to gvim)
color = 'default'

# if you want to save the transformed in a file.
filename = ''

C++:


//激活创建OpenGL窗口
void EnableOpenGL()
{
    PIXELFORMATDESCRIPTOR pfd;
    int iFormat;

    ghDC = GetDC( ghWnd );

    ZeroMemory( &pfd;, sizeof( pfd ) );
    pfd.nSize = sizeof( pfd );  
    pfd.nVersion = 1;      //版本,一般设为1
    pfd.dwFlags =   PFD_DRAW_TO_WINDOW | //一组表明象素缓冲特性的标志位
        PFD_SUPPORT_OPENGL;
    pfd.iPixelType = PFD_TYPE_RGBA;   //明象素数据类型是RGBA还是颜色索引;
    pfd.cColorBits = 32;     //每个颜色缓冲区中颜色位平面的数目,对颜色索引方式是缓冲区大小
    pfd.cDepthBits = 16;
    pfd.iLayerType = PFD_MAIN_PLANE; //被忽略,为了一致性而包含的

    iFormat = ChoosePixelFormat( ghDC, &pfd; );//选择一个像素格式

    SetPixelFormat( ghDC, iFormat, &pfd; ); //设置到DC中

    ghRC = wglCreateContext( ghDC );    //创建绘图描述表
    wglMakeCurrent( ghDC, ghRC );     //使之成为当前绘图描述表
}

java:



//: holding/AddingGroups.java
package holding; /* Added by Eclipse.py */
// Adding groups of elements to Collection objects.
import java.util.*;

public class AddingGroups {
  public static void main(String[] args) {
    Collection collection =
      new ArrayList
(Arrays.asList(1, 2, 3, 4, 5));
    Integer[] moreInts = { 6, 7, 8, 9, 10 };
    collection.addAll(Arrays.asList(moreInts));
    // Runs significantly faster, but you can't
    // construct a Collection this way:
    Collections.addAll(collection, 11, 12, 13, 14, 15);
    Collections.addAll(collection, moreInts);
    // Produces a list "backed by" an array:
    List
list = Arrays.asList(16, 17, 18, 19, 20);
    list.set(1, 99); // OK -- modify an element
    // list.add(21); // Runtime error because the
                     // underlying array cannot be resized.
  }
} ///:~


javascript:



// This function recursively looks at Node n and its descendants,
// converting all Text node data to uppercase
function upcase(n) {
    if (n.nodeType == 3 /*Node.TEXT_NODE*/{
        // If the node is a Text node, create a new Text node that
        // holds the uppercase version of the node's text, and use the
        // replaceChild() method of the parent node to replace the
        // original node with the new uppercase node.
        n.data = n.data.toUpperCase();
    }
    else {
        // If the node is not a Text node, loop through its children
        // and recursively call this function on each child.
        var kids = n.childNodes;
        for(var i = 0; i < kids.length; i++) upcase(kids[i]);
    }
}


再来个不一样的,delek主题+行号的Python效果:
 


1
 2 MOD_ALT = 0x0001
 3 MOD_NONE = 0x000
 4 MOD_CONTROL = 0x0002
 5 MOD_SHIFT = 0x0004
 6 MOD_WIN = 0x0008
 7
 8 # the syntax support you want in the trayicon menu
 9 syntaxSupport = ["c", "cpp", "python", "java", "javascript"]
10
11 # the global hotkey define,the modifier can be used is listed above.
12 modifier = MOD_WIN
13 hotkey = 'Z'
14
15 # do you need display line number before every line?
16 isLineNumber = True
17
18 #------------------------------------------------------------------------------------
19 # you need not change these below at most time if you don't know what it is.
20 # the color theme you want use. (corresponding to gvim)
21 color = 'delek'
22
23 # if you want to save the transformed in a file.
24 filename = ''

 

呵呵,效果不错吧?唯一的问题是因为使用了vim,所以总是会弹出一个gvim的窗口,有点影响视觉效果,但是好在不影响使用,对于支持的语法我重新贴一下:

2html          a2ps
a65            aap            abap           abaqus
abc            abel           acedb          ada
aflex          ahdl           alsaconf       amiga
aml            ampl           ant            antlr
apache         apachestyle    arch           art
asm            asm68k         asmh8300       asn
aspperl        aspvbs         asterisk       asteriskvm
atlas          autohotkey     autoit         automake
ave            awk            ayacc          b
baan           basic          bc             bdf
bib            bindzone       blank          bst
btm            bzr            c              calendar
catalog        cdl            cdrdaoconf     cdrtoc
cf             cfg            ch             change
changelog      chaskell       cheetah        chill
chordpro       cl             clean          clipper
cmake          cmusrc         cobol          coco
colortest      conaryrecipe   conf           config
context        cpp            crm            crontab
cs             csc            csh            csp
css            cterm          ctrlh          cuda
cupl           cuplsim        cvs            cvsrc
cweb           cynlib         cynpp          d
dcd            dcl            debchangelog   debcontrol
debsources     def            denyhosts      desc
desktop        dictconf       dictdconf      diff
dircolors      diva           django         dns
docbk          docbksgml      docbkxml       dosbatch
dosini         dot            doxygen        dracula
dsl            dtd            dtml           dtrace
dylan          dylanintr      dylanlid       ecd
edif           eiffel         elf            elinks
elmfilt        erlang         eruby          esmtprc
esqlc          esterel        eterm          eviews
exim           expect         exports        fasm
fdcc           fetchmail      fgl            flexwiki
focexec        form           forth          fortran
foxpro         framescript    freebasic      fstab
fvwm           fvwm2m4        gdb            gdmo
gedcom         git            gitcommit      gitconfig
gitrebase      gitsendemail   gkrellmrc      gnuplot
gp             gpg            grads          gretl
groff          groovy         group          grub
gsp            gtkrc          haml           hamster
haskell        haste          hastepreproc   hb
help           hercules       hex            hitest
hog            hostconf       html           htmlcheetah
htmldjango     htmlm4         htmlos         ia64
ibasic         icemenu        icon           idl
idlang         indent         inform         initex
initng         inittab        ipfilter       ishd
iss            ist            jal            jam
jargon         java           javacc         javascript
jess           jgraph         jproperties    jsp
kconfig        kix            kscript        kwt
lace           latte          ld             ldapconf
ldif           lex            lftp           lhaskell
libao          lifelines      lilo           limits
lisp           lite           litestep       loginaccess
logindefs      logtalk        lotos          lout
lpc            lprolog        lscript        lsl
lss            lua            lynx           m4
mail           mailaliases    mailcap        make
man            manconf        manual         maple
masm           mason          master         matlab
maxima         mel            messages       mf
mgl            mgp            mib            mma
mmix           mmp            modconf        model
modsim3        modula2        modula3        monk
moo            mp             mplayerconf    mrxvtrc
msidl          msmessages     msql           mupad
mush           muttrc         mysql          named
nanorc         nasm           nastran        natural
ncf            netrc          netrw          nosyntax
nqc            nroff          nsis           objc
objcpp         ocaml          occam          omnimark
openroad       opl            ora            pamconf
papp           pascal         passwd         pcap
pccts          pdf            perl           pf
pfmain         php            phtml          pic
pike           pilrc          pine           pinfo
plaintex       plm            plp            plsql
po             pod            postscr        pov
povini         ppd            ppwiz          prescribe
privoxy        procmail       progress       prolog
promela        protocols      psf            ptcap
purifylog      pyrex          python         qf
quake          r              racc           radiance
ratpoison      rc             rcs            rcslog
readline       rebol          registry
remind         resolv         reva           rexx
rhelp          rib            rnc            rnoweb
robots         rpcgen         rpl            rst
rtf            ruby           samba          sas
sass           sather         scheme         scilab
screen         sd             sdl            sed
sendpr         sensors        services       setserial
sgml           sgmldecl       sgmllnx        sh
sicad          sieve          simula         sinda
sindacmp       sindaout       sisu           skill
sl             slang          slice          slpconf
slpreg         slpspi         slrnrc         slrnsc
sm             smarty         smcl           smil
smith          sml            snnsnet        snnspat
snnsres        snobol4        spec           specman
spice          splint         spup           spyce
sql            sqlanywhere    sqlforms       sqlinformix
sqlj           sqloracle      sqr            squid
sshconfig      sshdconfig     st             stata
stp            strace         sudoers        svn
syncolor       synload        syntax         sysctl
tads           tags           tak            takcmp
takout         tar            tasm           tcl
tcsh           terminfo       tex            texinfo
texmf          tf             tidy           tilde
tli            tpp            trasys         trustees
tsalt          tsscl          tssgm          tssop
uc             udevconf       udevperm       udevrules
uil            updatedb       valgrind       vb
vera           verilog        verilogams     vgrindefs
vhdl           vim            viminfo        virata
vmasm          voscm          vrml           vsejcl
wdiff          web            webmacro       wget
whitespace     winbatch       wml            wsh
wsml           wvdial         xbl            xdefaults
xf86conf       xhtml          xinetd         xkb
xmath          xml            xmodmap        xpm
xpm2           xquery         xs             xsd
xslt           xxd            yacc           yaml
z8a            zsh           

支持的颜色主题有:
blue  
color
darkblue  
default  
delek  
desert  
elflord  
evening  
koehler  
morning  
murphy  
pablo  
peachpuff  
ron  
shine  
slate  
torte  
zellner

支持的颜色主题,语言语法都是通过config来配置,其实操作就是选择好语言,copy,Win+Z,paste。。。。。。。。。。

 

原创文章作者保留版权 转载请注明原作者 并给出链接

write by 九天雁翎(JTianLing) -- www.jtianling.com

阅读全文....

C和Python程序员的JavaScript学习指南(译)

write by 九天雁翎(JTianLing) -- www.jtianling.com

讨论新闻组及文件

 原文来自:《JavaScript for C & Python programmers

》,经过作者Canis允许后翻译,即使转载请附带此链接。



前言:很少干翻译的事情,累,辛苦,不能发挥,典型的费力不讨好,最近无聊,想在网页上显示动画(因为CSDN贴图不是不让贴了嘛),Java
applet虽然已经搞定了,但是总感觉这不是Java的世界,所以想大概学习一下JavaScript后再做决定,(-_-!从命名上看其实像是换成了
Java的脚本-_-!)网上类似JAVA for C++
programmers的文章很多,但是js类似的文章却很少,一方面可能的确java更加流行,另一方面可能一般的C/C++程序员与js的领域差的太
远,没有此需求,本文也不是为C++程序员写的,是为c和python程序员写的。

翻译完后,就个人感觉,本文甚至不能算是一个较好的指南,大概可以看做一个C,Python程序员学习
JavaScript时的抱怨,全文有太多作者的抱怨,针对JavaScript不寻常的语法提出了很多看法,用本文来全面的了解Javascript是
不太可能的,但是了解一下作为一个C,Python程序员,学习JavaScript应该注意哪些奇特的地方,也不是没有好处。



(以下我的注释都添加**开头,没有添加的为作者原注释)


以下是译文:



C和Python程序员的JavaScript学习指南

作者:Canis


  • 第一次发表: 2006年三月


  • 更新: 2008年七月 (添加了到 JSLint的链接)


  • 最后一次更新: 2009四月 (更好的删除Array items的方式)


 


    本文是对JavaScript的简单介绍。网上有很多类似的资料,但是大部分都是针对非程序员读者的,教导一些javascript的简单代码。那样也没有什么,因为很多从来不编程的人发现他们的网页需要一些js脚本的时候会需要这些。


    这篇文章提供了一些有关JavaScript的更深层次的信息。我假设你完全不懂javascript但是也假设你已经是一个称职的程序员,是"C-like"语言的程序员更好,并且懂一点python将很有帮助(**说的不就是我吗?-_-!)


    我将谈到任何我认为javascript不好的地方。(**原文用到object model 指的的是对象模型吗?作者是想说面向对象吗?我都没有在文章中看到)



JS不是JAVA

    
首先,一个常见的误解需要澄清,javascript从来与JAVA没有任何关系。JS以前甚至不叫js(另外,java以前也不叫java,但是那就是另外一个故事了
)。(**
链接中关于JAVA历史的第一句话就是:Java programming Language evolved from a language
named
Oak)这个Java的前缀完全是出于市场的因素,可能是因为(**这句没有看懂)这对他们互相的支持有意义,就像将他们的腿绑在一起,共同在市场上进
退。警告:本文包含JS特性这样的主题,假如你发现这对你来说太难了,你最好早点走吧。

  
 JS是一个动态语言,有着C语言一族的基本语法,但是却有很先进,看起来很像Python的数据类型。(**原文用Pythonesque,表示非常符
合Python设计思想和Python好的使用习惯,更通常我们用Pythonic一词),事实上,从很多方面来讲,它的很多行为也是这样的。

    
事实上,JS是有令人惊讶的强大的,表达能力强的语言,似乎应该能非常优雅的完成很多工作。

    然而,事实并非如此。

   它带来的是一个无尽的痛苦。陷阱潜伏在黑暗之中,等待时机,像野兽一样潜伏着,他们会吃掉你。(**作者此处提供了一个我无法打开的网页链接:http://www.scarygoround.com/shop-tshirts.php#bears)

    JS是一个彻头彻尾的混账语言,但是你却无法摆脱,同样的,浏览器的API也是同样的让人困惑。

通常来说,JS似乎遵循的是最大惊讶原则。(**-_-!)



网页的嵌入


  

 JS 可以单独运行(比如
Rhino

),(**Rhino是

JavaScript for Java,一个将js编译为java字节码运行的开源项目,我常认为犀牛书《JavaScript The Definitive Guilde》封面画了个雄壮的犀牛就是这个原因)或者嵌入到特定的hosts中,(比如Mac OS X Widgets

),但是,最通常的做法是将其嵌入到网页中,通过如下的形式:

 

 

html:

<script language='javascript'> // source code goes here </script>

或者:

html:

<script language='javascript' src='/path/to/source.js' />

在<script>的tag的scope内,普通的HTML语法不再有效。--<就是<,不是tag开始符,并且&就是&,不是指示一个字符的开始。(全文需要的HTML知识仅限于此)










基础语法

    
大部分的基本语法直接来自于C,所有下面的语法行为就像在C/C++/等语言(**等等中包括java,python-_-!)一样。

注释:  /* C */ 风格 和  // C++ 风格

条件:    if /then /else

选择:    switch /case /break default

while循环:    while /do /break /continue

for循环:    for /break /continue


    函数看起来与C类似,但是又一些不同。我们马上就会讲到。for的循环有一些我们需要关心的额外语法。类在js中是与其他大部分语言完全不同的。


    在语句结尾的分号: 你可以使用它们,但是他们是普遍是可选的,根据晦涩难解的的ECMAScript标准(呵呵--”标准化“版本的JS)的规则,js会自动的插入分号。它大概意思是,在缩进是比较清楚,没有二意性时,你不需要分号。



变量和声明

但是,不像Python,你确实需要声明一个局部变量的存在,只是不声明它是什么类型。这不是显而易见的,因为如果你没有声明一个变量,JS将自动声明它。

全局声明

    是的,JS采用了Python相反的决定(Python中除非明确的声明一个变量是一个全局变量,那么就是局部变量),JS中除非你明确的声明,不然都假设变量是全局的。


这样会有一些非常令人惊讶和恼火的
影响
特别是,如果你忘了声明一个变量,你的代码可能会很好地工作,直到它彻底的与其他代码搅在一起,因为其他代码使用了同样名字的全局变量。



或者你写了一些递归代码,接着可怕的嵌入一个意外的无限循环中 -- 可能会没有反应,甚至崩溃。你的浏览器不是足够智能化去跳出无限循环,唉。。。。。。



  
 是的,JS设计的时候就是要让某些事情花掉你95%的时间(还不是全部),当你忘记这些事情的时候,它可能静静的失败,也有可能崩溃,还有可能让整个执
行环境没有反应,使得你除了反复试验外没有办法去调试。感谢你,JS语言的设计者们! 感谢你,浏览器的实现者们!




  
 无论如何,你使用var关键字声明变量,比如:var msg = "hello" --
如果你不指定值,变量默认是未定义的。我建议你总是通过var声明你的变量,不管是不是真的需要,这样会更加安全。假如任何人知道一个JS的Lint工具
(**lint是一个很著名的C语言错误分析程序),让我知道并且我会在此添加一个链接。









Update: Martin Clausen指出存在
JSLint
。你需要仔细的调整你的options,另一方面,JSLint在其首页有一个强大的警告信息系统。











函数

JS的函数与C有些不同。他有两种形式,命名的及匿名的。命名函数与C中很像,但是,当然,没有任何参数或返回值的类型声明。在C语言中放置返回值的地方,使用function关键字。
function treeWalk(branch, visitor){
    visitor(branch)
    var i // not going to let you forget! you'll thank me later! 
    for (i in branch.children) {
        treeWalk(branch.children[i], visitor)
    }
}
在这里,visitor是一个传递进来的函数,我们将在以后展示怎么样做到。branch是一个用户定义的对象,我们假设它有一个名叫children的array。
此外,我相信你已经注意到这个不寻常的for结构。这是一个迭代器版本。(**事实上是典型的for each语法规则)
迭代器语法:
for (<variable> in <variable of container type>) <statements>

迭代器遍历容器中的每一项,然而,这是另一个经典的JS令人烦恼的情况,它仅返回每个成员的索引。假如容器是一个有10个元素的array,它可能返回整数0到9(看看下面关于arrays的内容)。假如容器是一个hash-map,它将返回每个key。

这就需要一个额外的,多余的哈希查找过程,因为似乎没有(就我目前所找到的)对应Python的for(key,value) in container: statements循环的语法。




匿名函数是非常明显的,你仅仅忽略名字。

function(param1, param2 ...) {
statements 
}

就其本身而言,这是毫无价值的,上面的就是表达一下语法。你可以将整个定义放在任何地方,包括变量赋值(**其实意味着JS中函数是第一类值(first class)),下面的例子是相同的定义一个名为mul的函数。

var mul = function(a,b) { return a*b }


一个不同的例子,用了前面定义的的treeWalk函数:(**即将匿名函数直接作为函数参数传递)




treeWalk(rootNode, function(item) { logDebug(item) } )




类型,转换,比较

显然,你有数字类型(double float -- JS事实上没有整数) ,字符串类型,并且他们就像你预期的那样使用。然而,JS有一个偶然(或者说令人惊讶的)的在各种类型中间转换的倾向。例如,

rhino 
示例:

js> 2 + 2

4

js> "2" + 2

22

js> "2" * 2

4

"+" 被用于数学的加法和字符串连接。

当至少有一个字符串参数时,它将其他类型全部转换为字符串然后连接,但是其他数学操作却是转换字符串到数字。哈哈!结合操作符的优先级将带来更多的额外奖励的乐趣和混乱。

rhino 示例

:




js> "2" + 2 + 2
222
js> 2 + "2" + 2
222
js> 2 + 2 + "2"
42
js> "2" + 2 * 2
24
js> "2" * 2 + 2
6
js> ("2" + 2) * 2

44

 

深深的,慢慢的呼吸...
 这一切总会过去的。。。。。。。(**真不愧是最大惊讶。。。。)


Oh yeah,并且JS的比较操作符也是诡异的,==转换类型,===不会


rhino 示例
:
js> 2 == 2
true
js> 2 == "2"
true
js> 2 === 2
true
js> 2 === "2"
false
并且,注意字符串比较中给你的意外,然而,这还不算太坏,假如每个操作数都是数字,转换将会向着数字的方向。
rhino 示例
:
js> 4 > 2
true
js> 4 > 22
false
js> 4 > "22"
false
js> "4" > "22"
true

噢!并且JS将会很高兴的无声无息的将无效的数字转换为NaN,并且会将这种行为贯穿在整个代码中。。。。

rhino 示例
:
js> "2" * 2
4
js> "two" * 2
NaN
js> x = "two" * 2
NaN
js> 4 * x
NaN
记住,深深的,慢慢的呼吸......






Arrays 和 Hashmaps

JS内建Arrays and hashmaps 。假如你仅仅需要声明一个含有一些初始化数据的array和hashmap,JS有一个可以直接从Python中拷贝数据过来的语法 -- 中括号用于list,大括号用于hashmap。

rhino 示例:
js> myArray = [1, 2, 3, 17, 23, 42, 69]
1,2,3,17,23,42,69
js> myHash = {"key": "value", "key2": "value2"}
[object Object]
两者都是用方括号索引:
rhino 示例:(接上面)

js> myArray[3]
17
js> myHash["key2"]
value2
你可以混合和配对使用任何集合数据类型,比如每个值都是一个array或者hashmap。对于key来说,似乎转换所有的数据类型到字符串形
式并且这样使用--所以JS中的语句myHash[fred] = value似乎像python中的myDict[repr(fred)] =
value一样。
你可以用同一种语法删除两种类型的元素,delete语句:
rhino 示例:(接上面)
js> myHash["key2"]
value2
js> delete myArray[3]
true
js> delete myHash["key2"]
true
(delete似乎总是返回true,是的,甚至你的索引超出范围,是的,甚至这里没有一个对应的key。我不知道为什么)


(**rhino 示例:(接上面)

js> myHash["key2"]
js> delete myHash["key2"]
true
)
然而,你应该注意,如果你从一个array中移出了一个元素,它会留下一个空白(undefined),并且所有的其他元素仍然有同样的索引。
rhino 示例:
js> myArray
1,2,3,,23,42,69
js> print(myArray)
1,2,3,,23,42,69
js> print(myArray[3])
undefined
js> print(myArray[4])
23
(那个两个连续的逗号不是一个印刷错误,undefined的值转换到字符串类型时没有任何输出,但是这里仍然有一个在array为其保留的‘槽’)
现在,当您在一个数组迭代, 它会自动跳过这样的空插槽:
rhino 示例:
js> for (i in myArray) print(i)
 1
 2
 3
 23
 42
 69



(**在我这里的  rhino 运行效果与原作者的有所区别,就像作者前面说的那样,返回的是key

js> for( i in  myArray) print(i)
0
1
2
4
5
6
js> for( i in  myArray) print(myArray[i])
1
2
3
23
42
69
真正神奇的是此时跳过undefined值的方式是直接忽略了,连个空行都没有

虽然如此,对于许多人物来说,有一个undefined的值在array中可能是非常痛苦的事情,所以你会发现,写一个函数用于“压缩”一个array,或者返回一个指定索引忽略的副本是有用的,给你一个删除+压缩的函数。(代价是一次不必要的复制操作)

JS:

function deleteArrayItem(source, index){
    var result = new Array()
    for (i in source) 
        if (i != index) 
            result.push(source[i])
    return result
}

Update:Reddit 用户 davidsickmiller
提到一个我忽略的array方法,当我在2006年写下这个的时候。Array::splice(**切片函数)从数组中移出一个范围,返回移出的元素,并且,将原来的array打包。(**此处原作者用packing,其实就是去除undefined的元素)



rhino 示例:

js> myArray = [1, 2, 3, 17, 23, 42, 69]
1,2,3,17,23,42,69
js> myArray.splice(3,1)
17
js> myArray
1,2,3,23,42,69
(**这样的形式可以看到,事实上不想要undefined的数据时根本就不应该使用delete操作,事实上,对于已经有了undefined数据时,splice也是很有用的,可以直接移出undefined的数据)
Arrays会自动的扩展,当你插入一个超出索引范围的值的时候。并且,假如这个范围不是连续的,那么所有中间的“槽”都是undefined的。
Array有一个length成员变量,比如myArray.length,但是它不告诉你到底有多少元素存在,它告诉你有多少的"槽",用另外的话说,它总是返回你插入元素的最高的索引+1,假如你想要知道实际的元素数量,好的,你就不得不遍历array并且自己计数。
好了,我要走了,留下你自生自灭。。。。。。。。。。
















 


原创文章作者保留版权 转载请注明原作者 并给出链接

write by 九天雁翎(JTianLing) -- www.jtianling.com



阅读全文....