摘要:轉譯利用構建應用英文原文地址引入了來創建這使得可以訪問類庫我已經深入研究這塊并且編寫了一些今天這篇文章會講解一些基礎東西并且一步步的來創建一個小的上面有一個主題專門解釋用來代替創建自動化應用程序這是個非常激動的事情使用來構建自動化任務已經存
轉:[譯]利用js構建osx應用
英文原文地址
OSX Yosemite引入了js來創建Automation,這使得javascript可以訪問nativeOSX類庫,我已經深入研究這塊并且編寫了一些examples,今天這篇文章會講解一些基礎東西并且一步步的來創建一個小的example app.
WWDC 2014上面有一個JavaScript for Automation主題,專門解釋用javascript來代替applescript創建自動化應用程序,這是個非常激動的事情,使用applescript來構建自動化任務已經存在很長時間了,它的一些語法一直都不是很受歡迎.
在這個主題上,主持人講解了object-c bridge,這個是非常酷的東西,可以往javascript中導入任何底層的方法,例如,如用你想用標準的OS X控件來創建gui應用的話,你將要引入cocoa:
ObjC.import("Cocoa");
Foundation框架就跟它名字一樣,提供一些基礎模塊給osx app,它包含很多類以及接口,比如NSArray,NSURL,NSUserNotification等等,也許你對這些類不熟悉,但是根據字面意思大概就能知道它們的功能,這些類比較常用,所以可以直接在app中使用而不需要多帶帶導入它.它是默認就會加載的
我可以這么跟你說,只要是用objective-c或者swift創建的app,用javascript都可以做出來.
build and example app注意:你的操作系統要在yosemite開發版7+,下面的例子才可以正常運行
了解它的最好方法就是實踐,下面我們將要實現一個簡單的app(從你電腦上選取一個圖片并顯示它),效果如圖
制定一個app.js,包括一個窗口,一個文本標簽,一個輸入框和一個按鈕,對應的class名稱就是NSWindow,NSTextField,NSTextField和NSButton.
單擊選擇圖片文件按鈕,將會顯示一個NSOpenPanel,它將會顯示一個文件選擇窗口,我們還需要配置這個窗口的文件過濾屬性,只能選擇.jpg,.png或者.gif
當選擇一個圖片之后,將會在窗口中顯示出來,并且自適應圖片大小,窗口設置了一個最小的寬高以免圖片被裁剪
創建一個project打開Apple Script Editor應用程序,定位到Applications > Utilities,Script Editor并不是最好用的編輯器,但是現在有必要用它,它提供了很多的特性用來創建JS OSX APP,并且可以用來編譯和運行你的js osx app,它也可以添加些擴展文件像我們app需要的info.plist文件.我猜也許還有別的編輯器可以做同樣的事情,不過目前我還沒有找到.
創建一個新的文檔,定位到File->New或者使用命令cmd + n,首先要做的就是保存它為Application,定位到File->Save或者使用命令cmd + s,確認保存之前,有兩個選項需要注意下,看下圖:
文件格式選擇Application,選中Stay open after run handler
如果沒有選中Stay open after run handler,打開應用之后會一閃而過,然后自動關閉,這些網上都沒有什么教程,只是自己幾個小時摸索出來的.
現在應該是做些有意義的時候了
添加下面兩行代碼到你的編輯器中,然后運行它,定位到Script->Run Application或者opt + cmd + r.
Objc.import("Cocoa"); $.NSLog("hello feenan!");
這時候運行應用什么都沒有發生,唯一看到改變的就是全局菜單欄以及dock,因為應用名稱以及file,edit并排在菜單欄上,應用圖片顯示在dock上,這些都代表著應用已經在運行.
那么hello feenan!跑哪去了呢?$符號是什么,是jquery?,我們先把應用程序退出了,定位到File->Quit或者cmd + q,然后我們來找找NSLog輸出的內容.
打開Console app,定位到Applications > Utilities > Console,任何一個應用程序都可以記錄日志到console app中,它跟chrome,firefox,safari中的控制臺沒多大區別,主要的區別在于你可以使用它來調試應用程序代替website
在console app中有很多日志信息,你可以在右上角的輸入框中輸入applet來過濾日志,輸入applet到過濾框中之后,回到Script Editor中,再次運行應用程序,使用opt + cmd + r命令,控制臺信息如下圖
你是不是看到在控制臺中顯示hello feenan!了,如果沒有的話,退出應用程序再次運行看看,有時候我們忘記退出應用程序,代碼并沒有再次運行.
$符號是什么呢?$讓你能夠訪問Objective-C bridge.任何時候你需要訪問Objective-C其中某個類或者常量,你都可能使用$.foo或者ObjC.foo,后面還有講到關于使用$的其它一些方法.
Console app和NSLog是必不可少的工具,你將會不停的使用它們來調試你的應用程序,想要了解更多的信息,可以點擊NSLog example
創建一個窗口讓我們創建一個可以顯示并且有交互的窗口,代碼看起來像下面這樣
ObjC.import("Cocoa"); var styleMask = $.NSTitledWindowMask | $.NSClosableWindowMask | $.NSMiniaturizableWindowMask; var windowHeight = 85; var windowWidth = 600; var ctrlsHeight = 80; var minWidth = 400; var minHeight = 340; var window = $.NSWindow.alloc.initWithContentRectStyleMaskBackingDefer( $.NSMakeRect(0, 0, windowWidth, windowHeight), styleMask, $.NSBackingStoreBuffered, false ); window.center; window.title = "Choose and Display Image"; window.makeKeyAndOrderFront(window);
當這些都在合適的位置之后,我們運行它通過opt + cmd + r,然后我們可以說,只需要這么點代碼就可以啟動一個app并且打開一個窗口,而且我們可以移動,最小化和關閉它.
如果你跟我一樣沒有用Objective-C或者Cocoa來創建一個app的話,這些可以看起來有點難以理解,對我來說,這些方法名稱的長度有點難以接受,雖然我喜歡描述性的方法名稱,但是像Cocoa這樣的還是太極端了.
看上面的代碼其實就是javascript,就跟你編寫網站代碼一樣.
第一行中的styleMask是干么的呢?它提供了一些對窗口屬性設置的功能,有標題,關閉按鈕,最小化按鈕,這些選項都是常量,通過|符號來添加多個,|符號是C里的or操作符,不用去理解它的原理,只要知道它是用來并列多個選項的就足夠了.
這里有很多種樣式信息,想了解詳情的可以點擊the docs,NSResizableWindowMask將是接下來要使用的一個樣式,可以添加到上面的代碼中看看效果
這里還有一些有趣的語法需要你記住,$.NSWindow.alloc調用NSWindow里的alloc方法,但是并沒有在后面插入(),這種調用方式就跟js里獲取屬性一樣,但是js里調用方法不是這樣的,原來,在OSX JS中,假如方法沒有參數的時候,是不能在后面加入()的,否則它會出現運行時錯誤.所以以后當你發現有些事件并沒有像你期望中發生時就要檢查下console里的輸出內容了.
下一個要關注的事情是這個超長的方法:
initWithContentRectStyleMaskBackingDefer
讓我們來看看NSWindow的doc上講解這個方法的,你會發現有一點不同:
initWithContentRect:styleMask:backing:defer:
上面的描述相當于在Objective-c中創建下面的代碼
NSWindow* window [[NSWindow alloc] initWithContentRect: NSMakeRect(0, 0, windowWidth, windowHeight) styleMask: styleMask, backing: NSBackingStoreBuffered defer: NO];
注意下上面方法簽名中的:,當你想把一個Objective-c中的方法轉換成js的方法時,首先需要把:除掉然后把跟在后面的第一個字母大寫.當看到方括號[]里有兩項是,代表調用一個類或者對象的方法,NSWindow alloc表示調用NSWindow的alloc方法,換成js的話,需要在兩者之前添加一個.,像NSWindow.alloc這樣
我認為剩下的代碼足夠用來描述創建一個窗口并顯示它,我跳過了很多關于這方面的細節說明,這個需要很多時間用來閱讀相關文檔,不過你可以這些.當你做到顯示出一個窗口來已經不錯了,讓我們做更多的事情吧
添加控件窗口里還需要一個標簽,一個輸入框,一個按鈕,我使用NSTextField和NSButton來創建它,輸入下面的代碼然后運行你的app
ObjC.import("Cocoa"); var styleMask = $.NSTitledWindowMask | $.NSClosableWindowMask | $.NSMiniaturizableWindowMask; var windowHeight = 85; var windowWidth = 600; var ctrlsHeight = 80; var minWidth = 400; var minHeight = 340; var window = $.NSWindow.alloc.initWithContentRectStyleMaskBackingDefer( $.NSMakeRect(0, 0, windowWidth, windowHeight), styleMask, $.NSBackingStoreBuffered, false ); var textFieldLabel = $.NSTextField.alloc.initWithFrame($.NSMakeRect(25, (windowHeight - 40), 200, 24)); textFieldLabel.stringValue = "Image: (jpg, png, or gif)"; textFieldLabel.drawsBackground = false; textFieldLabel.editable = false; textFieldLabel.bezeled = false; textFieldLabel.selectable = true; var textField = $.NSTextField.alloc.initWithFrame($.NSMakeRect(25, (windowHeight - 60), 205, 24)); textField.editable = false; var btn = $.NSButton.alloc.initWithFrame($.NSMakeRect(230, (windowHeight - 62), 150, 25)); btn.title = "Choose an Image..."; btn.bezelStyle = $.NSRoundedBezelStyle; btn.buttonType = $.NSMomentaryLightButton; window.contentView.addSubview(textFieldLabel); window.contentView.addSubview(textField); window.contentView.addSubview(btn); window.center; window.title = "Choose and Display Image"; window.makeKeyAndOrderFront(window);
如果應用跑起來沒問題的話,將會看到下面的效果,你可以在輸入框中輸入內容,按鈕點擊沒有任何效果,不過我們后面會加些東西上去
看到上面的代碼是不是對添加有些疑惑,到底怎么實現的呢?textFieldLabel和textField非常相似,它們都是NSTextField的實例,在這里都是通過相似的方法實現的,當你看到initWithFrame和NSMakeRect時,它們是創建ui元素的好方法,NSMakeRect就跟它的名字一樣,用來創建有一定大小的方形用給定的位置信息(x, y, width, height).這就相當于創建了Objective-c中的結構體信息,在js中我們引用它為一個對象或者hash,或者一個dict,擁有自己的鍵值對.
創建完輸入框這后,給它賦一些屬性,Cocoa并沒有多帶帶創建標簽的方法,所以這里我們利用NSTextField,禁用它的編輯屬性并且設置的背景樣式來模擬標簽效果.
假如是正常的輸入框的話,其實只需要設置一行代碼就可以了
對于按鈕我們使用NSButton類,跟輸入框一樣,創建它也需要先畫一個矩形,不過這里有兩個屬性需要強調下:bezelStyle和buttonType.它們的值就是常量,主要用來控制按鈕的渲染以及有什么樣式信息,想了解更多的信息,可以點擊docs,我也有一些關于不同樣式的按鈕例子,example app
最后的事情就是通過addSubview把這些控件添加到我們的window中去,剛開始我調用window.addSubview(theView),但是并沒添加起來,最后發現只能添加其它用NSView創建的實例,我不知道為什么會這樣,但是想要添加NSWindow創建的實例的話,只能調用window.contentView.addSubview(theView).官方文檔上是這樣描述的,NSView對象是窗口里最高的訪問層次.
給按鈕添加代碼當點擊按鈕的時候,我想顯示一個面板出來,上面列出本地的文件信息,做這些之前,讓我們先添加一個日志信息熱熱身.
在javascript中添加事件一般是給對象添加監聽,但是在Objective-C中沒有這樣的概念,在它這里叫消息通訊,你得發送一個包含方法名的消息給目標對象,目標對象收到這個包含方法名的消息之后才能決定做什么,也許我說的不是很準確,但是大概就是這個意思
首先我們要做的就是添加一個target和一個action,target是發送給action的一個對象,也許現在還沒什么意義,但是后面我會增加更多的代碼,先增加下面的一部分代碼,用來更新按鈕屬性的:
... btn.target = appDelegate; btn.action = "btnClickHandler"; ...
appDelegate和btnClickHandler還沒存在,所以要先創建它們,在下面的代碼有提供,然后代碼中還增加了注釋用來告訴你把這些新的代碼添加到何處
ObjC.import("Cocoa"); // New stuff ObjC.registerSubclass({ name: "AppDelegate", methods: { "btnClickHandler": { types: ["void", ["id"]], implementation: function (sender) { $.NSLog("Clicked!"); } } } }); var appDelegate = $.AppDelegate.alloc.init; // end of new stuff // Below here is in place already var textFieldLabel = $.NSTextField.alloc.initWithFrame($.NSMakeRect(25, (windowHeight - 40), 200, 24)); textFieldLabel.stringValue = "Image: (jpg, png, or gif)"; ...
運行app,然后在console中查看是否有顯示Clicked!,如果顯示了,說明代碼沒問題,否則檢查下代碼跟文中是否有區別,然后仔細看下console中的錯誤信息.
Subclassing(子類)ObjC.registerSubclass是干什么的呢?Subclassing是創建一個子類的方法,它可以繼承一個父類.也許這個名稱叫的不專業,還請多多包含.registerSubclass需要一個參數,它是一個對象,成員可以包含name,superclass,protocols,properties,methods,我不敢保證這里列出了所有的成員,不過你可以看release notes.
一切看起來挺不錯的,但是上面的代碼做了什么呢?因為并沒有寫superclass屬性,所以默認繼承NSObject,它是所有Objective-c里的基類, 設置name屬性方便后面我們用$或者Objc來引用它.
$.AppDelegate.alloc.init會創建一個AppDelegate類實例,需要再次注意的是,alloc和init后面并沒有插入(),因為我們并沒有傳任何參數.
Subclass methods(子類方法)可以通過一個字符串內容來創建一個方法,比如上面的btnClickHandler,然后給它提供一個對象參數,成員包括types和implementation,官方文檔上并沒有說明types數組應該包括什么,但是經過我不斷嘗試感覺它的參數說明應該是這樣的:
["return type", ["arg 1 type", "arg 2 type",...]]
btnClickHandler沒返回任務東西所以設置return type為void,它需要一個參數,就是發送的對象,所以這里設置參數名為id,它可以表示任何對象.
想查看整個類型的列表信息,可以點擊release notes
implementation是一個普通的函數,在這里面可以寫javascript,同時可以訪問$符號以及外面定義的變量.
使用protocols的問題在子類中可以實現Cocoa protocols,但是我發現在你腳本中使用protocols數組,應用程序會停止而且沒有任何錯誤,我寫了一些example and explanation來說明這些問題,有興趣的可以看一看.
Choosing and displaying images(選擇并顯示圖片)我們準備打開一個面板,選擇一個圖片,然后顯示它,更新btnClickHandler的implementation函數,代碼如下
... implementation: function (sender) { var panel = $.NSOpenPanel.openPanel; panel.title = "Choose an Image"; var allowedTypes = ["jpg", "png", "gif"]; // NOTE: We bridge the JS array to an NSArray here. panel.allowedFileTypes = $(allowedTypes); if (panel.runModal == $.NSOKButton) { // NOTE: panel.URLs is an NSArray not a JS array var imagePath = panel.URLs.objectAtIndex(0).path; textField.stringValue = imagePath; var img = $.NSImage.alloc.initByReferencingFile(imagePath); var imgView = $.NSImageView.alloc.initWithFrame( $.NSMakeRect(0, windowHeight, img.size.width, img.size.height)); window.setFrameDisplay( $.NSMakeRect( 0, 0, (img.size.width > minWidth) ? img.size.width : minWidth, ((img.size.height > minHeight) ? img.size.height : minHeight) + ctrlsHeight ), true ); imgView.setImage(img); window.contentView.addSubview(imgView); window.center; } }
首先我們創建NSOpenPanel一個實例,如果你從來沒有打開過文件或者保存操作,那么默認顯示的面板是活動文件列表.
我只想打開圖片文件,所以需要過濾文件列表,設置allowedFileTypes屬性,它是一個NSArray類型,我們創建一個js數組allowedTypes來給它賦值,但是我們需要轉換成NSArray類型,通過$(allowedTypes),這是bridge橋的另外一種用法,可以通過這個方法在js和Objective-c之間進行類型轉換,想轉換Objective-c類型為js中對應的類型的話,使用$(ObjCThing).js方法.
打開面板通過panel.runModal方法,這個代碼會馬上執行,你可以點擊取消和確定,然后我們可以通過返回值來判斷你點擊是哪個,當點擊確定返回的是$.NSOKButton.
另一個需要注意的是panel.URLs,通常我們訪問js中的數組元素是通過[],因為URLS是NSArray類型,所以不能通過[]來訪問,這里提供了objectAtIndex方法來訪問,它跟[]的效果是一樣的.
只要我們獲取到圖片的URL,那么我們就可以創建一個NSImage實例,這里有一個專門的方法
initByReferencingFile
跟創建其它ui元素一樣, 這里我們創建一個NSImageView實例來顯示圖片
我們想窗口的大小能夠自適應圖片的大小,但是不能低于最小寬高,為了設置窗口的大小,這里調用setFrameDisplay方法
我們通過圖像視圖來包裝圖片,然后把它添加到window視圖中,因為窗口大小改變了,所以需要重新計算居中顯示.
Tidbits(花絮)迄今為止,我們已經在Script Editor中創建了一個app并用命令opt + cmd + d運行它,也可以像其它app一樣,雙擊它的應用圖標來運行.
你也可以修改app圖標的路徑,替換/Contents/Resources/applet.icns就行,想訪問應用的資源文件,可以右鍵app選擇Show Package Contents就行.
WHY I’M EXCITED(為什么我這么激動?)我對它的潛能感到非常激動,下面是我已經想到的一點觀點.當Yosemite正式發布之后,很多人可以坐下來開發原生app了,使用最普通的編程語言,而且不用下載或者安裝別的東西,假如你想的話連xcode也可以不用安裝,完全降低了進入app開發的門檻,這簡直太瘋狂了.
我知道有很多大型應用程序通過腳本是辦不到的,我也沒有說腳本是創建app的唯一方式,但是我們可以讓一些人創建一些小的app為他自己或者別人.當一個團隊整天在命令行下工作的不舒服時,我們可以為它們創建一個gui程序;當需要快速,可視化的創建或者修改配置文件時,也可以為它創建一個小的app.
當然也有其它編程語言可以辦到,像python和ruby也可以訪問到低層api,然后創建app.只是利用javascript來創建app顯的更與眾不同,這簡直有點顛覆我們的思想.這感覺就像一些網站敲響了桌面上的門,apple讓這個門解鎖了,我完全被它吸引了.
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/85490.html
摘要:巔峰人生年老兵思路上的轉變,遠比單純提升技術更有價值本文節選自趙成教授在極客時間開設的趙成的運維體系管理課,是其對自己十年技術生涯的回顧與總結。趙成教授來自美麗聯合集團,集團旗下兩大主力產品是蘑菇街和美麗說,目前負責管理集團的技術服務團隊。 showImg(https://segmentfault.com/img/remote/1460000012476504?w=1240&h=826...
摘要:同時也是對無法完整支持的一種補充。不過我們只能通過的返回結果判斷執行成功還是失敗。另外,雖然有垃圾回收機制,但開發者還是應當在使用完數據后手動刪除相應的。最新版版本已經修復上述問題。 原文地址:https://github.com/Microsoft/... 本文介紹 Napa.js 的核心概念,帶領大家探索 Napa.js 是如何運轉起來的。關于它的由來和開發初衷,可以閱讀 這篇文章...
摘要:簡介是一個開源的應用容器引擎,讓開發者可以打包他們的應用以及依賴包到一個可移植的容器中,然后發布到任何流行的機器上,也可以實現虛擬化。該虛擬機需要安裝操作系統以便在您機上運行。 Docker簡介 Docker 是一個開源的應用容器引擎,讓開發者可以打包他們的應用以及依賴包到一個可移植的容器中,然后發布到任何流行的 Linux 機器上,也可以實現虛擬化。 docker的英文本意是碼頭工人...
閱讀 788·2021-10-09 09:44
閱讀 692·2019-08-30 13:55
閱讀 3153·2019-08-29 15:07
閱讀 3218·2019-08-29 13:09
閱讀 2413·2019-08-29 11:10
閱讀 1289·2019-08-26 14:05
閱讀 3591·2019-08-26 13:57
閱讀 2206·2019-08-23 16:42