顯示具有 Web Dev 標籤的文章。 顯示所有文章
顯示具有 Web Dev 標籤的文章。 顯示所有文章

2012年11月7日 星期三

[node.js] 在heroku上用bower管理前端第三方元件

在做web applications時,或多或少都會使用到像是jQuery, bootstrap這類的第三方元件,使用已經host在既有CDN 的版本(比如說google的)是一個不錯的方法,但不是每一種都有這類方案,一個個從各網站下載回本地端又不是那麼容易管理,bower就是為此出現的,bower是一個類似npm的軟體,由Twitter所開發出來且是開放原始碼,所不同的是,npm是管理node.js相關的套件,但bower是管前端的

舉一個例子,你可以利用bower來安裝jQuery :
bower install jQuery
bower會把jQuery 安裝到"components/jQuery" 目錄裡

在npm , 你可以把所有相關的模組設定在package.json中,這樣就不用一個個下"npm install mypackage"這樣的指令了,bower也有類似的設計,只是為了避免跟package.json造成混淆它採用的檔名是component.json,內容跟package.json差不多:
{
    dependencies: {
      "JQuery": "*"
    }
}
在Heroku deploy node.js app有個很方便的地方是,當你做git push 後,它會根據package.json自動幫你安裝相關的node.js 模組,但不幸的是它並不支援bower,只支援npm

還好Heroku很有彈性,可以藉由buildpack來自己加這部份

buildpack API其實還蠻簡單的, 基本上只要準備三支shell script:
  • bin/detect
  • bin/compile
  • bin/release
大部分的東西, 大多在"bin/compile"裡完成的

自己做的buildpack也只要放github, 然後將自己app的buildpack指定到這來即可 (使用heroku toolbelt):
  • 新建的程式使用自己的buildpack: 
    heroku create myapp --buildpack https://github.com/heroku/heroku-buildpack-ruby
  • 既有的程式更換buildpack:
    heroku config:add BUILDPACK_URL=https://github.com/heroku/heroku-buildpack-ruby
當然, 不需要重頭建立自己的buildpack, Heroku官方的buildpack都有open source在github上, 如Node.js buildpack, 所以只要fork出來修改即可, 因此我從Node.js buildpack fork出一個新的buidlpack, 主要的修改是:

  1. 安裝bower
  2. 找出所有的component.json並用"bower install"安裝套件
本想說 "1" 應該蠻簡單的, 因為bower也是用node.js開發的, 應該只要用npm install bower就搞定了, 但事實是, 這樣會碰壁, 因為bower採用了lodash , lodash跟Heroku的npm似乎八字不合, 安裝過程到lodash的post-install就會失敗, 也沒明顯的錯誤, 只說這應該是套件問題, 而去查了lodash, 也有人報過類似的issue, 但作者卻以lodash沒問題應該是npm的問題把這issue給關了(鬼打牆?), 但:
  • 自己電腦上安裝bower或lodash都沒問題
  • 在自己的package.json加上lodash, deploy到Heroku也一樣掛
  • 在package.json裡設定engine裡的npm版本到最新的1.1.65(跟自己電腦上一樣版本), 也一樣不行

那...就不得不懷疑是npm問題了, 查了原本buildpack寫的, Heroku的node.js和npm其實是拿他們自己放在S3的binary package來安裝的, 可疑!

因此就使用了下一招 - 用npm來裝npm

用Heroku系統內的npm來裝npm是可行的, npm install npm的結果是, 會有一份最新的npm被安裝到node_modules底下, 可以用"node_modules/.bin/npm"來呼叫這版本, 因此就加上了:
node_modules/.bin/npm install bower
Bingo! 這樣可行, bower一樣被安裝到 node_modules/.bin底下, 加上 "2"的邏輯到"bin/compile"後就大功告成了,

有修改的部份只有"bin/compile", 比對一下commit history就可以知道我修改過啥, 中間有做了很多實驗, 所以有不少垃圾commits, 可以忽略

如果有需要這版本buildpack, 也歡迎利用:

https://github.com/julianshen/heroku-buildpack-nodejs




2012年11月5日 星期一

[node.js] 用npm安裝放在github上的module

剛剛為了想要做一個東西, 需要用到Flickr API, 本想要拿Passport.js來處理Flickr OAuth的部份,但卻發現, FLickr Strategy的部份不但年久, 而且permission這部份居然是hard code "read", 由於我想做的東西是要write, 其實是可以靠overwrite "userAuthorizationURL"來達到這目的, 不過這樣不好看, 所以就乾脆自己fork一版來改 (Github真是好物來改)

改完後的一個問題是, 原本可以用"npm install passport-flickr"來安裝這module, 但由於我fork出來的並未註冊在npm, 所以就得用另一個方式:
npm install git://github.com/julianshen/passport-flickr.git#master
這方式是直接把git url給npm, 不過要deploy上heroku就麻煩了, 還好寫在package.json裡也是沒問題的
"dependencies": {    "passport-flickr": "git://github.com/julianshen/passport-flickr.git#master"}
這還蠻簡單的, 只是把原先寫version的地方換成git URL就好, 在Heroku上也是沒問題的

題外話: 剛deply到heroku上時發現express 3.0.1無法安裝, 原因是我忘了在package.json裡註名我node.js的版本, 這種狀況下, Heroku會自動幫你挑v0.4.7版

2012年10月8日 星期一

在node.js使用underscore.js

太早起床,只好隨便研究點東西 :P
 
underscore.js對javascript來說是一個蠻好用的工具, 在前端應用程式的開發中也很常被利用到,但同樣以javascript為基礎開發的node.js, 能否直接使用underscore.js呢?

嘗試以下面的程式碼用node.js跑:

require('./underscore.js');                                                                

var a = [1, 23, 6, 11, 25, 12, 33, 11, 4];
 
 _.forEach(a, function(v) {
     console.log(v);
});


執行結果會得到"ReferenceError: _ is not defined"
打開underscore.js的原始碼一看, 發現了這一段, 其實它是有針對node.js做手腳的:


// Export the Underscore object for **Node.js**, with
// backwards-compatibility for the old `require()` API. If we're in
// the browser, add `_` as a global object via a string identifier,                     
// for Closure Compiler "advanced" mode.
if (typeof exports !== 'undefined') {
     if (typeof module !== 'undefined' && module.exports) {
       exports = module.exports = _;
     }
     exports._ = _;
 } else {
      root['_'] = _;
 } 

也就是, 在browser時, "_"是一個global object, 但在node.js則不是, 原本的範例如果改成這樣就可以了:


var _us = require('./underscore.js');                                                                

var a = [1, 23, 6, 11, 25, 12, 33, 11, 4];
 
 _us.forEach(a, function(v) {
     console.log(v);
});


這樣一來, 前後端也可以共用同一份underscore.js了

補充: 類似的工具還有async這個, 也是前後端都可以應用


----- 10/11 補充 -----
寫這篇的時候真的是沒睡飽, 沒注意看underscore放在github上的內容, 它已經弄好node module了, 所以只要:
npm install underscore
就可以裝好了, code裡面也只需要用"var _us = require('underscore')" 

2012年5月7日 星期一

[筆記] Self published B2G app

B2G並未一定要開發者把應用程式發表到一個特別的app store, 網站也可以自行加一個install按鍵, 讓使用者把你的網站當應用程式裝到手機內

首先, 你必須要先有個manifest, 目前manifest並沒強制的檔名, 但文件裡建議叫 xxxx.webapp, 以下是範例內容:

 

{ "name": "MyTestApp", "description": "test app", "launch_path": "/", "icons": { "128": "/img/icon.png" }, "developer": { "name": "Julian Shen" } }

 

然後在網頁內加上檢查是否安裝, 以及安裝的程式碼, 範例如下:

var manifestUrl = 'http://localhost:3000/manifest.webapp'; var app = navigator.mozApps;  function checkInstalled() { var request = app.getSelf(); request.onsuccess = function() { if(request.result) { //installed console.log('installed'); document.getElementById('installmsg').style.display='none'; } else { //not installed console.log('not installed'); } }; }  function install(cb) { var request = app.install(manifestUrl); request.onsuccess = function() { alert('installed'); };  request. function() { alert('Not installed'); } }
這邊要注意的是manifestUrl必須是full path而不是只有相對位置, 寫相對位置, 它還是讀的到, 但會出parse error, 之前害我搞半天一直以為是格式問題, trace了一下Webapps.js, 發現可能是判斷install origin有問題
目前安裝介面很醜:
_2012-05-08_2
安裝完就可以在Home screen看到它的存在了:
_2012-05-08_2

2012年4月12日 星期四

[筆記] Practice: Open graph with Facebook JavaScript API

Source: https://bitbucket.org/julianshen/videomag/overview

Demo URL: http://growing-wind-8625.herokuapp.com/top

Server: Play framework 2.0 on Heroku

Open graph object: Youtube video, Action: watch

It's not a problem to add youtube video since it already contains open graph meta data in page. All we have to do is call "/me/video.watches?video=youtube_link" with POST.

To post activity to timeline, the permission "publish_actions" is required. 

The result on timeline:

_2012-04-13_2

2012年3月7日 星期三

[筆記] "Unsafe JavaScript attempt to access frame with URL" error with Facebook Javascript API

The following example shows how to create a simple Facebook login button with Facebook javascript API:

Normally, this page would be reloaded if user finished authorized your app. But it won't be reloaded in Chrome due to the follwing error:

Unsafe JavaScript attempt to access frame with URL https://s-static.ak.fbcdn.net/connect/xd_proxy.php?version=3#cb=f363e58d9&origin=http%3A%2F%2Fwww.ab.com%2Ffcb27588&relation=opener&transport=postmessage&frame=fac4c0f18&access_token=AAAEHZBpoK3B0BALv8Cf96gaKEnn4ZBtXgjCOQCFFhVDL4K0rZAE2NGDcqZCPlZByNNKEDmYSGKHQv8jAUM2yGZCHkOcyXUKhoZD&expires_in=0&signed_request=g7sYd_XQx-EBPtQQGk-0ISRMrUDnT4L4WHAmlNsVFBU.eyJhbGdvcml0aG0iOiJITUFDLVNIQTI1NiIsImNvZGUiOiJBUURKYkZBNjZuemduSDBBN0dXWmd2SGxZZkQxQVp4Qm1MSnc0Z3ppUV9FQ25VeEYyZmcyTmJGcFh3UHZDeWhyYnFrT1RxNmRVc2xsb0d4dlFCd290UjRyV0ZsSnJtVjFoNy1QUTlJbTRROXh3MmNKMGU1b0NReWtmT1ZZcXhMUDRVRldVSDgwVU5ic1BLTHdyNk80cFlSVVItQzNIYjhyRWgxek40ZFEzSkpreXhBcU1KSHljcWxETzh2dF96T3ZpN0EiLCJpc3N1ZWRfYXQiOjEzMzExMzg3MjQsInVzZXJfaWQiOiIxMTI5MjgzNDM3In0 from frame with URL http://www.ab.com/login.html. Domains, protocols and ports must match.

Some people use FB.getLoginStatus() to check if user authorized app at page loaded. If page could be reloaded, it's no problem. However, in this case, it's not. 

There are several questions on StackOverflow related to this:

http://stackoverflow.com/questions/3577947/facebook-gives-unsafe-javascript-attempt-to-access-frame-with-url-error-in-chr

http://stackoverflow.com/questions/8013008/facebook-authentication-unsafe-javascript-attempt-to-access-frame-with-url

http://stackoverflow.com/questions/3010170/unsafe-javascript-attempt-to-access-frame-with-url-error-being-continuously

But none of these solves my problem. I don't want to solve this with introduce PHP sdk. I would like to solve it with pure front end codes. 

Therefore, I find 3 solutions:

  1. Ignore the error and register login event to solve the problem:
  2. Add attribute "show-faces="true"" to <fb:login-button> to solve
  3. Add attribute "render-in-iframe="true"" to <fb:login-button> to solve. This is a bad idea since this is an undocumented attribute.

In solution 2 and 3, login-button seems to be placed into an iframe. And 3 would display an incomplete login button (bug?)

2011年12月27日 星期二

[筆記] Connect java client to a node.js server with Thrift

Thrift是由Facebook開發的一套RPC system, 廣泛的被很多軟體應用, 像是HBase, Hadoop, Cassandra... 也支援了許多語言 , 可以跨語言做RPC
但....Thrift的document真是么壽的少...少的實在有夠可憐....本來想說實作一個java client連到node.js寫的server, 搞半天東挖挖西挖挖後才搞定.....
首先是安裝到我的mac就把我搞暈了(加上感冒本來就暈), 一開始我用macport裝, 但裝完後, 找不到libthrift.jar, 所以只好上網站抓source來build, 所幸可以只build java library的部份, 不用整個thrift都build, 這部份倒不難, 用ant就搞定了
裝完thrift後, 寫好程式, build java版本時就出了問題了, javac說TClient不是個interface, 追進code才發現, 我自己build的jar是最新版的 (0.8), 但port幫我裝的是0.6, 產生的codes完全不相容, 後來改用brew裝(就是不想從頭從Source build), 終於是0.8版的了(port上的也太舊了吧)
這邊實作一個簡單的加法器, Server side是跑node.js, Client是java, 有空在來試試別的組合, 建一個新檔"computer.thrift", 內容如下:


這邊定義一個簡單的Service - "Computer", 只含有一個方法"add", 內容很簡單, 就是用來回傳a+b的值, "namespace java com.thrift.gen"的用途就是指定產生的java code的package, 如果沒指定就是沒package, i64指得就是64bit integer
接下來就是要用thrift產生對應的程式碼:
thrift --gen js:node --gen java computer.thrift
這行指令同時產生for node.js的版本(在gen-nodejs目錄), 跟java版本
Server implementation
先安裝thrift module (for node.js)
npm install thrift
實作server.js:


在Server裡面實作add, 由於是asynchronous的, 所以結果由callback回傳
至於Client端的部份也蠻簡單的:


在網路上找到的sample, 都使用TSocket, 但用TSocket在這範例, client/server都會掛掉, 追了server code發現, node.js server default應該是用Framed transport, 所以在Client端加上TFramedTransport就OK了

2011年11月5日 星期六

[筆記] Sencha Touch + Facebook Graph API

Sencha Touch 2.0在OO的包裝上做的還算不錯, 把MVC的角色切分的還蠻清楚的, 以List為例, 大概就像這樣:

_2011-11-05_5
但它的document實在很糟糕, 光看他的document大概僅知道, Proxy可分為兩類Client(Memory, Local Storage ... )與Server (AJAX, JSONP ...)

但如果是要用Facebook Java script SDK去存取Facebook Graph API這類, 似乎就不知道怎歸類了, 如果直接用JSONP去存取Graph API, 則碰到Authentication error.. orz

那...就只好寫一個Proxy了, 像這樣:

https://gist.github.com/1341345.js?file=gistfile1

以下是一個使用這範例存取使用者自己的Facebook Group的範例:

https://gist.github.com/1341348.js?file=gistfile1

2011年10月20日 星期四

[筆記] Tabs on JQuery Mobile and Sencha touch

JQuery mobile跟Sencha touch都是蠻完整的mobile web framework, 兩者各有擅長, 比較起來以開發的角度我比較喜歡JQuery所標榜的"Write less, Do more"的哲學下的架構, 而不喜歡Sencha touch把一堆html寫到code裡面去, 但Sencha touch又有比較好的UI look and feel

以tab panel為例,

Sencha touch:

Photo_11-10-21_1_33_28
jQuery mobile:
Photo_11-10-21_12_25_52

這兩個作法大異其趣 

Sencha 的HTML 內容

裡面除了script以外根本就是空的, UI的創建放在app.js(以這範例而言)裡如下:

https://gist.github.com/1301828.js?file=gistfile1

Tabs的內容在哪? items裡分別就是兩個tab, html直接以字串的形態寫在裡面, 老實說, 我覺得這很醜, 也容易出問題, 如果頁面的內容是相當複雜的, 這樣並不是很好

 

再看看jQuery Mobile的作法:

這份source code有點偷懶, 剪剪貼貼過來的, 不過其實就這麼一個html, 並不需要寫額外的javascript code, 乾淨多了  

如果也可以用類似的寫法寫Sencha touch的UI似乎應該會比較好一點, 像是這樣寫:

做了個實驗, 剛寫下這段code把上面那段轉成跟第一個範例一樣的畫面:

https://gist.github.com/1301855.js?file=gistfile1

看來如果再多層包裝其實也不用醜醜的通通把UI hard code到js codes裡面去

2011年9月28日 星期三

[筆記] Building social networking service with DIASPORA* on Heroku

_2011-09-29_12

Looks familiar? It's not Google+. It's DIASPORA. An open source project that implements a distributed social networking service. This project was announced on APRIL 24, 2010 (Just right after Facebook f8 2010 that is at APRIL 21). Alpha version was released at NOV 23, 2010 (two days before my birthday :P). 

According to this, Mark Zuckerbug also donated to it just because it's a cool idea.

It's built on Ruby on rails. So it might be not so difficult to port it to Heroku platform which is a nice RoR host (althrough it still took me some time). It might be easier than build from scratch on a Linux. 

I'm a newbie to these two (Heroku and DIASPORA). I'm also not familar with RoR. Here records steps I tried. 

create an application on heroku

First, you need to create an application on Heroku. There are several platform stack on Heroku. Cedar stack might be the newest and most powerful one. It supports several languages and frameworks. And It also makes it very easy to deploy RoR applications. 

You need to install heroku CLI before creating an application. And use "heroku login" to login to your heroku account.

Run the following command to create a new cedar application (assume application name is "mysocialy") --

heroku create --stack cedar mysocialy

After the application created, you'll have a url "http://mysocialy.herokuapp.com/" for your site. And a git repository: git@heroku.com:mysocialy.git

import source codes

Get DIASPORA* source codes from git hub:

git clone git://github.com/diaspora/diaspora.git

Get your source codes from Heroku git repository (you'll get an empty folder):

git clone git@heroku.com:mysocialy.git

If you have trouble to download from Heroku. Try to use "heroku keys:add" to add your ssh public key and try again.

Copy all files except ".git" folder from "diaspora" to "mysocialy".

Initial configuration

Make new configuration files from example.

cd config

mv application.yml.example application.yml  

mv database.yml.example database.yml

Edit application.yml. Make "pod_url" to your host. In this case:

pod_url: "http://mysocialy.herokuapp.com/"

Deploy codes

Add and commit files:

git add .

git commit -am 'initial import'

Push them to Heroku

git push origin master

The coolest thing that Heroku does is that it makes deploy codes so easy. All you need to do is to push your codes to its git repository. And it could also install all related modules for you (that is configured in Gemfile). Super easy.

Ok, I must admit that I lie a little bit. There might be some problems. After I pushes all codes to Heroku, I found there is an error that it couldn't find 'pg' (postgresSQL). Looks like it does not install into gem.

I found the answer that it might be problem with gem version. Need to run "bundle install" at local.  This will generate a new Gemfile.lock. Push this new file to Heroku might solve this problem

Ok, that's all?

Not sure if I missed anything (I might). Anyway, what I did is alive at "http://mysocialy.herokuapp.com/".

2010年12月23日 星期四

[筆記] Touch Draw with in HTML5

-- 前言 --

Safari2Phone做兩天就膩了(天呀, 我無可救藥), 昨天想到玩點別的

第一階段目標, 用"<canvas>" tag + JQMobile (其實非必要) 做了一個Tocuh Draw, 就是手指在Canvas上畫圖, 像這樣(這是測試結果):

----

測試平台: Nexus One (Android) + WebView

碰到的問題:

Canvas可以很正常的在Android上跑沒問題, 但主要是event的問題, 本來用addEventListener('mousemove', draw)的方式, 沒想到在mobile環境全部怪怪的

手機上的event應該是touchstart, touchend, touchmove, 其中touchmove跟mousemove有點不同, mousemove不用mousedown就會被觸發, 但touchmove卻是在有touch的狀況下才會發生

touch event的點也不一定只有一點, 所以有event.touches, 而這是一個array, 裡面放每一點座標, 像是clientX, clientY之類的

Canvas的部份, 用moveTo->lineTo來畫這些經過點就可以達成了(筆記: 下一步做一個queue來儲存)

2010年12月22日 星期三

[Safari Extension] Safari2Phone

其實這是我自己無聊寫的啦, 這是我第一隻Safari Extension, 不過我不打算publish到Safari extension網站, 放這邊就好, 請點此下載

這是從Chrome2Phone porting來的, 如果不知道Chrome2Phone是啥東西, 可以先看一下這段影片:

要玩這個東西, 首先你必須要有隻Android手機....

然後Android手機上必須裝Chrome2Phone:

裝完後, 你會發現有個烏七媽黑的button 在你的tool bar上(抱歉, 沒做美工... orz)

當你想把正在看的網頁送到手機上, 就按下這個button, 如果沒有登入, 會有一條新的bar上面有顯示訊息叫你登入, 點選連結即可登入

 

Chrome extension跟Safari extension本質上蠻像的, 所以要po蠻容易的, 基本上花了我一晚上的時間, 從開始看Safari extension的文件開始, 到這個可以正常工作, 今晚回來後又稍微小修點bug再release

Chrome2Phone本身原理也蠻簡單的, 加上也open source, server端跟Chrome extension端都有source可看, extension端其實只是透過XMLHttpRequest把資料傳送過去, 這部份甚至可以原封不動reuse

讓我花比較多時間才發現的是, redirect url居然hard code在server端, 導致我的extension最後登入完只能被導到醜醜的圖, 本來想透過injected script讓他碰到那張圖的url就導到我的page, 但似乎好像沒辦法在tab內開啟extension package裡的一個HTML

總之, 先這樣, 有空再修好了, 繼續找看看有沒其他好玩的來玩..

2010年12月21日 星期二

開發Safari Extension (之一)

現在的Browser好像不開放給人家掛一些外掛就落伍了, 大部分的Desktop Browser也都有提供這樣的功能, Apple的Safari也不例外

開發Safari Extension其實不難, 如果有開發過Google Chrome browser extension的話, 可能還更好上手, 架構差不多類似, 文件也沒很多, 大概花個半小時看一下文件就應該可以上手了 

不過, 前置作業的門檻可能就大了一點了!

首先....你必須要有台.... Mac ... 光聽到這需求感覺就很鳥, 我沒在Windows底下的Safari開發, 所以我不確定有沒方法解決, 不過我相信, 至少要能找到Keychain的替代方案才可以! 所以, 用Mac是最保險的!

然後你必須上蘋果的開發者網站去申請一個Safari developer的帳號, 好消息是, 這不像iOS developer方案是要錢的, 它是...完全免費

有了帳號後就要申請憑證, 申請憑證先要透過keychain這隻MacOS內建的程式去產生certificate request (CSR)檔, 如下(中文是憑證授權要求憑證...好爛的翻譯):

把CSR檔上傳後就可以產生憑證, 記得下載回來安裝, 不過這邊有點要注意一點, 用Chrome browser上傳總是會失敗, 用Safari就沒問題了, 光這部份搞了我一個晚上... orz

要開發Safari Extension的起始點是Extension Builder(延伸功能建構器), 打開你的Safari然後從"開發人員"->"顯示延伸功能建構器"

打開後從"+"的地方新增新的Extension, 它會讓你指定你Extension要存放的目錄, 之後只要把你的程式還有資源(圖檔等等)都copy到這目錄即可

先設定一下你的Extension的基本資料:

此外別忘了設定存取權限, 如果你的Extension要存取比預設多的資訊

接著設定一下幾個主要的元件, 以及資料庫所需要的容量大小(如果你會需要儲存資料的話)

這邊稍微簡單介紹幾個基本的元件, 以後有空再細寫

基本上一個Safari extension可以由以下的幾種東西構成

  1. Global page : 不會被顯示出來的一個背景頁面, html檔案, 通常用來提供function而非顯示
  2. Tool button : 工具列上的一個按鈕 (由新增工具列項目那邊新增)
  3. Context menu item : 按滑鼠右鍵叫出的選單(中翻譯成特色選單)
  4. Extension bar(中文翻譯成"列") : 一整列工具列, 可以由一個Html檔案來構成
  5. Injected script : 像是Chrome Browser的content script, 嵌入到網頁的java script
  6. Injected style : 嵌入到網頁的style sheet

基本上這些都可以在extension builder這邊設定, 不過看API, 也應該可以由程式動態控制去設定, 大概組合這六樣就可以簡單寫出一個Safari extension了

2010年12月19日 星期日

很久之前無聊寫的Chrome Extension

Chrome Extension其實還蠻簡單的, 之前無聊試寫了一個, 其實也不是啥大東西, 就只是去氣象局抓張最新的衛星雲圖回來

沒啥code, 隨便把它開放出來好了:  https://bitbucket.org/fishuman/taiwansatimage/src

2010年12月8日 星期三

[筆記] 產生 Web page thumbnail (續前篇)

前篇提到利用CutyCapt來產生Web page thumbnail, 不過畢竟CutyCapt是C++寫的, build出來也是一個執行檔, 寫完上一篇就有股衝動想用PyQt來做, 反正一樣可以利用Qt/WebKit, 原理應該相同的

沒想到, 真的那麼簡單, 只用了二十幾行code就可以辦到了

這邊主要利用到的一個class就是 QWebPage , 透過這個class我們就可以很簡單的達成這任務

為了程式的重複利用性, 我把它包裝成一個class PageRender:

class PageRender(QObject):

    def __init__(self, url, outfile, scale):

       QObject.__init__(self)

       self.outfile = outfile

       self.scale = scale

       self.web = QWebPage()

       self.web.mainFrame().load(QUrl(url))

       self.connect(self.web, SIGNAL("loadFinished(bool)") ,self.loadFinished)

 

    def loadFinished(self, b):

        print "load finished"

        self.web.setViewportSize(self.web.mainFrame().contentsSize())

        image = QImage(self.web.viewportSize(), QImage.Format_ARGB32);

        painter = QPainter(image)

        self.web.mainFrame().render(painter)

        painter.end()

        thumbnail = image.scaledToWidth(self.scale)

        thumbnail.save(self.outfile)

        app.exit(0)

 在Constructor那邊做的就是把loadFinished跟我們實際處理save to thumbnail的function connect在一起

而在loadFinished那邊就是實際render到image的部份, 這邊要注意的是:

self.web.setViewportSize(self.web.mainFrame().contentsSize())

這是把QWebPage的View port size設成跟實際content一樣大, 後面接著我們就可以建立QImage跟其相對映的painter, 然後用QWebFrame的"render()"把它實際輸出到image

最後scale到我們想要的大小(pixels)

這邊是一個實際call它的範例:

p = PageRender("http://www.yahoo.com.tw", "sssss.png", 200)

結果:

實際的程式可以在此下載

2010年12月7日 星期二

[筆記] 產生 Web page thumbnail

剛突然想弄一個可以產生Web page thumbnail的service

第一個想到的是能不能用Webkit做一個, 不過, 自己寫, 有點太麻煩了, 想說應該有人做過, 後來就找到這個: CutyCapt 

這軟體是用qt寫的, 也不會很複雜, 只有六百多行 (有點想用PyQt仿一個 :P), 也很容易就可以build起來了(只要qmake; make), 但記得要裝libqt4-dev就是了

使用方式很簡單:

CutyCapt -url=http://julianshen.posterous.com -out=julian.png

但產生的檔案, 是一整個頁很大一張, 像這樣(點開可以看大圖) :

這樣就不能算是thumbnail了吧?

所以只好拿ImageMagick來剪成200x200的Thumbnail

我用的command是這樣:

convert -resize 200 -shave 0x200 julian.png julian_small.png

出來的結果:

再找時間包裝成一個web app吧~~

2010年12月5日 星期日

A pattern learned from memcached

晚上快速翻了一變 memcached , 想了一些想法, 趁還沒忘之前先寫下來再去睡好了

memcahced的設計概念說實在的簡單到不行, 說簡單不是說他不好或是沒什麼, 反而這麼一個簡單的設計, 幫助真的會不小

memcached簡單說了就是一個分散式key-value based data cache, 資料是分散放在很多台電腦的RAM裡面, 因為是key-value based, 所以說起來就是一個大的hash table, 並不需要複雜的query, 只需要O(1)就可以取得資料, 而且資料就在RAM中, 可以是相當的快

我們都知道一個事實: "IO costs", 如果每次都需要從database query資料, 那所花的時間, 包含SQL處理的時間, 以及IO的時間會是相當長的, 這點不管是Web application或是在mobile上(尤其現在SQLite被大量使用)都通用

因此使用RAM來cache query result as long as possible會是tuning這類的performance的其中一步, 這觀念應該是很多人都想的到

不過這樣得pattern可能得考慮的:

  1. 怎樣把一個常用且複雜的query對應到一個key-value pair
  2. cache的存續時間
  3. How to efficiently invalidate or update cache after data set changed

或許在Android的ContentProvider中可以引入memory caching的機制來減少IO的量

2010年12月1日 星期三

[筆記] Embed Youtube player

以往嵌入Youtube video到網頁中要貼一大堆醜醜的object tag像是:

<object width="640" height="385"><param name="movie" value="http://www.youtube.com/v/VIDEO_ID?fs=1&amp;hl=zh_TW"></param><param name="allowFullScreen" value="true"></param><param name="allowscriptaccess" value="always"></param><embed src="http://www.youtube.com/v/VIDEO_ID?fs=1&amp;hl=zh_TW" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" width="640" height="385"></embed></object>

後來股溝大神....提供了一個新的方式, 這方式不用太過複雜的tag (說穿了不過就是用iframe包裝起來), 而且會隨使用者設定選擇到底是用Flash還是HTML5 player :

<iframe class="youtube-player" type="text/html" width="640" height="385" src="http://www.youtube.com/embed/VIDEO_ID" frameborder="0">
</iframe>

底下是實際的範例: