2013年7月24日 星期三

[sozi] Inkscape 的簡報外掛:sozi - Windows 7 安裝及注意事項

不小心誤入進入貴哥的 sozi 介紹文[3]

一試很開心。發現中文介紹大多是 linux,於是我分享一下我在 windows 7 下的使用心得。

 

安裝

在官方介紹裡[1],目前只支援 32-bit,所以!

  1. 請安裝 inkscape 32-bit[6] 版本。在 windows 下,會被安裝在 C:\Program Files (x86)\Inkscape\
  2. 為了要更換 inkscape 的 python 版本到 python 2.7,請先安裝 python 2.7 for windows 32-bit[7] 到系統中。預設是安裝到 C:\Python27
  3. 安裝 LXML for Python 2.7 and Windows 32-bit[8] http://www.lfd.uci.edu/~gohlke/pythonlibs/#lxml
  4. 安裝 PyGTK 2.24 的 all-in-one 安裝檔 for Python 2.7 and Windows 32-bit[10]
  5. 把 C:\Python27 複製到 C:\Program Files (x86)\Inkscape
  6. 把 C:\Program Files (x86)\Inkscape\Python 改名為 C:\Program Files (x86)\Inkscape\Python26
  7. 把 C:\Program Files (x86)\Inkscape\Python27 改名為 C:\Program Files (x86)\Inkscape\Python
  8. 把 sozi-release-[...].zip[11] 解開,放到 C:\Program Files (x86)\Inkscape\share\extensions

搞定。啟動或重開 inkscape 就可以在 「擴充功能」 裡看到 sozi 了。

 

意外

如果發現有字型問題怎辦?

請到 C:\Program Files (x86)\Inkscape\Python\Lib\site-packages\gtk-2.0\runtime\etc\gtk-2.0

找到 gtkrc,加入以下文字:

style "user-font"
{
    font_name="mingliu 11"
}
widget_class "*" style "user-font"

font_name 可改成你的系統中,找得到的字型的「英文名字」。
(所以寫 細明體 會沒有用)
後面的 11 是預設點數。請隨意。

 

重要

sozi 的編輯畫面不能讓我改 title 怎辦?

其實,不只是 title 不能改,有許多文字輸入框,數字輸入框,遊標根本進不去。
image

只能按左下角的 New,然後按確定。難道只能這樣使用 sozi,沒有辦法了嗎?

不是!這時只要換個視窗,再換回 sozi 視窗,就會變得可以輸入了。
舉例:在上圖中,滑鼠先點到 Timeout (seconds) 的 1.00 的框框裡。此時看不到遊標。然後,按下工作列的顯示桌面,再按工作列的 Sozi 視窗。你會發現遊標出來了。

image

然後整個畫面就都可以正常輸入。

 

注意

sozi 的官網有提到,因為 Inkscape 的 plugin 系統的設計關係,以下的畫面千萬不要去按取消。

image

我是沒按過取消會怎樣。人家都說不要按了。

 

參考:

1. http://sozi.baierouge.fr/wiki/en:install#installation_on_windows

2. http://people.ofset.org/~ckhung/b/svg/sozi.php

3. http://newtoypia.blogspot.tw/2012/11/sozi.html

4. 貴哥的簡報 http://people.ofset.org/~ckhung/mm/

5. 貴哥的進階技巧 http://user.frdm.info/ckhung/b/svg/sozi.php

6. Inkscape download http://downloads.sourceforge.net/inkscape/inkscape-0.48.4-1-win32.exe

7. python 2.7 for windows 32-bit http://python.org/ftp/python/2.7.5/python-2.7.5.msi

8. lxml-3.2.1.win32-py2.7.exe http://www.lfd.uci.edu/~gohlke/pythonlibs/bm67sm2y/lxml-3.2.1.win32-py2.7.exe

9. lxml http://www.lfd.uci.edu/~gohlke/pythonlibs/#lxml

10. pygtk-all-in-one-2.24.0.win32-py2.7.msi http://ftp.gnome.org/pub/GNOME/binaries/win32/pygtk/2.24/pygtk-all-in-one-2.24.1.win32-py2.7.msi

11. sozi-release-13.05-21064303.zip http://sozi.baierouge.fr/wiki/_media/dl:sozi-release-13.05-21064303.zip

2013年7月18日 星期四

[git][dulwich]在 windows 才會有的權限認定問題

前一陣子試著使用 dulwich 來操作 .git 裡面的資料,例如上傳到 changeset 到 remote、下載 changeset 到 local。看起來都很正常。但是,把檔案從 .git 裡面拿出來放,就會判定某些檔案被改變,我去檢查內容,自己使用 sha,md5 對兩個檔案檢查,結果顯示兩個檔案內容完全一樣。我想,dulwich 這麼多人用,難道我又遇到「沒人會遇到」的問題嗎?

另外我使用 tortoisegit 來檢查其內容,一樣也是說相同,但是用 dulwich 拉回來的 .git,跟 tortoisegit 拉回來的 .git 對於相同檔案,卻是一個說相同,一個說有改變。這可搞死我了,難道要放棄使用 dulwich 嗎?

後來翻到一個舊的 tortoisegit 的 issue:https://code.google.com/p/tortoisegit/issues/detail?id=812

Allow to change git file permission mask / attributes (especially executable bit)

才知道,dulwich 可能也是中同一個招數。git 是有檢查 Read, Write, Execute vs User, Group, Other 的。於是就可以解釋,為何只要是 script 或是 linux 的執行檔才會出現內容相同,但被判定有改變。在 windows 的檔案權限是 666,跟原來的不一樣了,也沒辦法改成 755。不曉得 tortoisegit 怎麼解決這問題的…。我這裡暫時停止研究了。

[git]學習 TortoiseGit - revert

原本有在用 hg,還以為會很順利地就會使用 git。

結果,等到真正開始管專案,第一個遇到的就是,revert 呢?

因為原本很習慣地,只要有臨時改過某個檔,要回到原狀,就很簡單地「指」著那個檔按右鍵,選revert

image

在下圖按下 revert 就解決了。

image 

但是,在 tortoisegit 我眼花啦~~~~~!!

我找了兩天啊!可說是上天下海用 google 去找,怎麼沒有人會有這種問題呢?

大概沒有人跟我一樣眼睛被蚵仔擋到,就在這兒啊,怎麼會有人要問這種問題呢?

image

其實還真有…

http://stackoverflow.com/questions/8011538/git-pulling-only-certain-files

2013年7月16日 星期二

[javascript]javascript如何列舉dictionary所有的key?

繼上次對於 dictionary 的操作,還少了一件事。那就是列舉所有的 key 值。

這件事沒法子由 instance 的 method 來找,於是使用 Object.keys() 這個功能來處理。

var a = {'key1':'a','key2':'b','key3':'c'};

var b = Object.keys(a)

console.log(b)

//會得到 ["key1", "key2", "key3"]

參考:

http://stackoverflow.com/questions/10654992/how-to-get-collection-of-keys-in-javascript-dictionary

2013年7月10日 星期三

[webapi]微軟 ASP.NET Web API Self Host 的應用(2)

(本篇程式是用 vb.net 寫成)

前一篇留下一個問題,若是要提供服務 .html, .js, .css, .jpg, .png, .gif ... 等等,我想到的辦法是自行撰寫程式來處理。在參考了[1] 的 route 用法,我在 config.Routes.MapHttpRoute 多加 4 個 route。img、css、js 分別提供 圖檔、css、js 三大類檔案的服務。而 default 則是提供第一個頁面的服務。

config.Routes.MapHttpRoute("img", "img/{name}", New With {.controller = "Img", .action = "g"})
config.Routes.MapHttpRoute("css", "css/{name}", New With {.controller = "Css", .action = "g"})
config.Routes.MapHttpRoute("js", "js/{name}", New With {.controller = "Js", .action = "g"})
config.Routes.MapHttpRoute("API", "{controller}/{action}/{id}", New With {.id = RouteParameter.Optional}) ' vs2010 just can not IntelliSense....
config.Routes.MapHttpRoute("default", "", New With {.controller = "Home", .action = "g"})

接下來則是加入 ImgController, CssController, JsController, HomeController 這四個 class

Public Class ImgController
    Inherits ApiController
    Dim accept_type As Dictionary(Of String, String) = New Dictionary(Of String, String) From {{".jpg", "jpg"}, {".png", "png"}, {".gif", "gif"}}
    <HttpGet()> Public Function g(ByVal name As String) As HttpResponseMessage
        Dim content As New StreamContent(New System.IO.FileStream(Request.RequestUri.LocalPath.Substring(1), IO.FileMode.Open))
        Dim ext As String = System.IO.Path.GetExtension(Request.RequestUri.LocalPath).ToLower()
        If accept_type.ContainsKey(ext) Then
            content.Headers.ContentType = New MediaTypeHeaderValue("image/" & ext)
        End If
        Dim response = New HttpResponseMessage() With {.Content = content}
        'response.Headers.CacheControl = New CacheControlHeaderValue() With {.MaxAge = New TimeSpan(1, 0, 0)}
        Return response
    End Function
End Class

Public Class CssController
    Inherits ApiController
    <HttpGet()> Public Function g(ByVal name As String) As HttpResponseMessage
        Dim content As New StreamContent(New System.IO.FileStream(Request.RequestUri.LocalPath.Substring(1), IO.FileMode.Open))
        content.Headers.ContentType = New MediaTypeHeaderValue("text/css")
        Dim response = New HttpResponseMessage() With {.Content = content}
        'response.Headers.CacheControl = New CacheControlHeaderValue() With {.MaxAge = New TimeSpan(1, 0, 0)}
        Return response
    End Function
End Class

Public Class JsController
    Inherits ApiController
    <HttpGet()> Public Function g(ByVal name As String) As HttpResponseMessage
        Dim content As New StreamContent(New System.IO.FileStream(Request.RequestUri.LocalPath.Substring(1), IO.FileMode.Open))
        content.Headers.ContentType = New MediaTypeHeaderValue("text/javascript")
        Dim response = New HttpResponseMessage() With {.Content = content}
        'response.Headers.CacheControl = New CacheControlHeaderValue() With {.MaxAge = New TimeSpan(1, 0, 0)}
        Return response
    End Function
End Class

Public Class HomeController
    Inherits ApiController
    <HttpGet()> Public Function g() As HttpResponseMessage

        Dim content As New StreamContent(New System.IO.FileStream("html/first.html", IO.FileMode.Open))
        content.Headers.ContentType = New MediaTypeHeaderValue("text/html")
        Dim response As New HttpResponseMessage() With {.Content = content}
        Return response
    End Function
End Class

 

接下來,就是把測試用的 first.html, .css, .js, .png 放到 html, css, js, img 目錄去。

以下是 first.html

<!doctype html>
<html>
<head></head>
<title>web browser</title>
<body>
<link href="css/site.css" rel="stylesheet" />
<script src="js/jquery.js" type="text/javascript" ></script>

<script type="text/javascript">
$(document).ready(function(){
//alert("jquery alert works")
});
</script>
<p>
this is html test
</p>
<p>
<input type="text" id="txtboxTest" />
</p>
<p>
<button id="btnTest">click me</button>
</p>
<script type="text/javascript"> $(document).ready(function(){
$('#btnTest').click(function(){
$('#txtboxTest').val('jquery click works!');
})
}); </script>
<p>
<img class="imgstyle" src="img/glyphicons-halflings.png" />
</p>
</body>
</html>

以下是 site.css

body { background-color: #0000ff }
.imgstyle {border:2px solid #00ffff;background-color: #ffffff}

而 jquery 及 png 圖檔還請自行準備。執行後,以 IE 來看。

image

下圖是用 chrome 來開。

image

這樣的程度,應該可以處理小小應用程式的需求,使用 SPA (Single Page Application) 的觀念來做,也就不需要多個 html 檔。若有其他需要,只需新增 route 及 controller 即可。

但是,這樣的程式,拿給我爸這種老人家,要先點一個執行檔,再開瀏覽器,然後輸入網址,他一定會嫌。在黑大的文章[2]底下有個留言說:

# GATTACA said on 04 June, 2013 10:44 PM
SignalR也有SelfHost可用啊,

ip過濾也可以只限自己127.0.0.1連線,

我現在所有單機版的操作介面都只剩下一個填滿的WebBrowser,啟動後再去讀Html檔案進來;

美工依照原本的WinForm排版Html順便美化,

程式設計師再去填SignalR JavaScript Client與Knockout ViewModel,

如果有用到COM元件,其callback事件處理後透過SignalR更新到Html上,也沒有背景執行緒更新主執行緒WinForm畫面的問題;

雖然是較極端的做法,但是可以擺脫微軟悲情的UI更迭(MFC->WinForm->WPF...),

避免程式設計師將業務邏輯綁死到UI元件上,

Html美工又很好找,

可說是利大於弊啊

對啊,這樣就會跟 app.js 一樣,看起來像是原生桌面程式有什麼不好的呢?

原來的程式執行起來長這樣,看來就想把它關掉。

image

現在,請在 Form1 加上一個 WebBrowser。然後在 httpserver.OpenAsync.Wait() 這一句的後面,加上

WebBrowser1.Navigate(http://localhost:32767)

在程式裡調好 Form1 寬度及寬度。接著在 WebBrowser1_DocumentTitleChanged 加上

Private Sub WebBrowser1_DocumentTitleChanged(ByVal sender As Object, ByVal e As System.EventArgs) Handles WebBrowser1.DocumentTitleChanged
    Me.Text = WebBrowser1.DocumentTitle
End Sub

執行程式就得到:

image

嗯,我現在感覺滿意耶。

我猜想這概念已經存在這麼久,.NET 的社群裡也許已經有類似的程式。

我這隻程式應該只是個幼稚園。

如果想要程式的話,我最近會整理放上 github 讓大家下載來用。

更新,程式在此:https://github.com/rickyteng/washapp

現在要說有什麼缺點的話,應該就是想把 webbrowser 換成 chrome 或 firefox 的引擎,html5 支援比較高。還有用 vb.net 寫 web api 的 route 及 對應的 controller,好像不如 c# 的簡易,c# 可以用 Get 當 function 名字,看起來若不特別指定,action 就是呼叫 Get(),vb.net 的我找不到對應的寫法,所以寫起來有點醜醜的。

終於用三天把兩個月來研究的「用 web 技術寫桌面程式」,的主題弄出一個段落。雖然,其實是 windows 桌面,因為使用者都在 windows 上,我技術上最後也跨不出 windows 平台。接下來就該寫出些有用的東西吧?

 

 

參考:

1. https://github.com/darrelmiller/HypermediaApiSite

2. http://blog2.darkthread.net/post-2013-06-04-self-host-web-api.aspx

[webapi]微軟 ASP.NET Web API Self Host 的應用(1)

(本篇程式是用 vb.net 寫成)

因為瀏覽器的能力大增,現在許多 GUI 的彩色糖果(新功能)都在瀏覽器上簡單易用。憑藉著 javascript 及瀏覽器就能做到原生桌面程式的事,而且不用在 c++ 裡打滾,也不用在意平台,真是一個全新的世界的來臨。

最早,mozilla 就試著使用 XUL 想要進入桌面程式,Google 也趁著 chrome 已融入大家的生活中的時機,推出 Chrome App。XUL 算是只使用前端瀏覽器引擎配合自有原生執行程式,Chrome App 則是採取與chrome 共生的執行環境來使用作業系統資源。接下來,有其他的人,想出自帶 server 端的方式來實現使用瀏覽器當 GUI 的解決方法,例如 app.js,利用 node.js 來當做程式的後端。

在黑暗執行緒看到的兩篇文章[1], [2]。才知道微軟出了個套件 ASP.NET Web API Self Host,可以當做 server 端的程式,可寫成 Console 程式,也可以寫成 WinForm。而這套件正好可以與 Chrome App、app.js 所提供的後端支援的功能相同。於是,我也來試試看是否能做出些什麼 app 來…。因為黑大已經示範了寫成 console 的程式,我就來試試 WinForm 的程式。黑大用的是 c#,我就來試試 vb.net 吧。

我還是一步步把畫面剪下來,免得以後我自己忘記,所以落落長。初學者才需要這樣。

首先,開啟一個 WinForm 的專案。

image

先存檔,然後開啟 NuGet。安裝 ASP.NET Web API Self Host。

image

以下是看到相依套件的授權。

image

實際安裝的套件如下:

image

接下來,安排程式碼。首先,在 Form1.vb 的最前面,加入 imports: (這樣可以少打幾個字。)

Imports System.Web.Http
Imports System.Web.Http.SelfHost

然後,加入兩個 form1 的變數,如下:

Dim config As HttpSelfHostConfiguration
Dim httpserver As HttpSelfHostServer

然後,加入一個 sub,是啟動 host 的部份,如下:

Sub hostinit()
    config = New HttpSelfHostConfiguration("http://localhost:32767")

    config.Routes.MapHttpRoute("API", "{controller}/{action}/{id}", New With {.id = RouteParameter.Optional}) ' vs2010 just can not IntelliSense....

    httpserver = New HttpSelfHostServer(config)

    'http://blog2.darkthread.net/post-2013-06-04-self-host-web-api.aspx
    httpserver.OpenAsync.Wait()
End Sub

選擇 Form1 的 New 事件,加入 hostinit(),使得 Form1 一出現就會接受來自 localhost:32767 的 request

Public Sub New()

     ' 此為設計工具所需的呼叫。
     InitializeComponent()

     ' 在 InitializeComponent() 呼叫之後加入任何初始設定。
     hostinit()
End Sub

在 Form1 的結束時,也要關掉 32767 的監聽。

Protected Overrides Sub Finalize()
    httpserver.CloseAsync.Wait()
    MyBase.Finalize()
End Sub

最後,也要加上 Controller 的物件,BlahController 來接受 request。(不要放在 Form1 裡面。我是放在另一個檔案 BlahController.vb)

Imports System.Web.Http
Public Class BlahController
    Inherits ApiController
    <HttpGet()> Public Function getDate() As String
        Return System.DateTime.Today.ToString("yyyy/MM/dd")
    End Function
End Class

完成了程式碼的安排後,還要記得黑大提醒的一件事,在 Windows 7、Windows 2008 這些作業系統開發時,因為權限控管較嚴,開 port 監聽需要較高權限來開放。我選擇了用 netsh 這個方法來開放權限。先開啟命令列(記得要用 administrator 權限開啟命令列)

netsh http add urlacl url=http://+:32767/ user=machine\username

記得上面的 machine 及 username 要換成你的電腦名稱及當下登入的使用者名稱。

接下來就可以試著啟動程式,開啟瀏覽器,輸入:

http://localhost:32767/Blah/getDate

如果瀏覽器的是 IE,會是這樣的結果:

image

按下開啟,內容是:

image

如果是用 chrome 的話,則會是:

image

 

出現了兩個不同的回傳內容。

 

接下來開始跟黑大不一樣了。

 

我們要開始問,如果我們要能夠服務 .html, .js, .css, .jpg, .png, .gif ... 等等一般的 web server 可以服務的檔案,該怎麼辦?

 

參考

1. 黑暗執行緒,不用IIS也能執行ASP.NET Web API,http://blog2.darkthread.net/post-2013-06-04-self-host-web-api.aspx
2. 黑暗執行緒,【範例】呼叫Self-Hosted ASP.NET Web API,http://blog2.darkthread.net/post-2013-06-05-post-to-web-api.aspx

3. http://www.dotnetcurry.com/ShowArticle.aspx?ID=896

4. http://stackoverflow.com/questions/17450846/is-it-possible-to-serve-a-web-page-from-a-self-hosted-web-api-in-a-windows-servi

5. https://github.com/darrelmiller/HypermediaApiSite

6. GetManifestResourceStream http://msdn.microsoft.com/en-us/library/System.Reflection.Assembly.aspx

7. return 405 while get .js http://stackoverflow.com/questions/17170277/jquery-get-returns-404-undefined-from-self-hosting-web-api

8. http://www.codeproject.com/Articles/455565/How-to-access-Manifest-Embedded-resources-from-an


類似 app.js 的平台,我試過的有:

google chrome packaged apps

appjs

還沒試過的平台:

node-webkit https://github.com/rogerwang/node-webkit

xul https://developer.mozilla.org/en/docs/XUL

Appcelerator Titanium http://en.wikipedia.org/wiki/Appcelerator_Titanium

Ember.js http://emberjs.com/

NACL https://developers.google.com/native-client/

 

VB.NET 的 IntelliSense 看不到 .Routes.MapHttpRoute 但 CS 可以。又多了一個換 CS 的理由。(逃)
http://forums.asp.net/t/1819836.aspx/1

2013年7月8日 星期一

[gae]增加對 svg 的支援

GAE 對於 svg 的支援,是把它當做 octet stream(application/octet-stream) 來處理。如果是這樣,當網址的結尾是 foo.svg 這種,就會得到下載的視窗,而不是想像的顯示圖案。

若是要讓它可以是顯示圖案,要讓 server 可以在 mime type 指定成 image/svg+xml。

因此,就要在 app.yaml 告訴 GAE 了。

以下是範例:[參考1]

- url: /svg/(.*\.svg)
  static_files: svg/\1
  upload: svg/(.*\.svg)
  mime_type: image/svg+xml

 

以下就是我測試用的 svg,是用 sozi 做的。使用 chrome, firefox 可以看。

http://rickystockinfo.appspot.com/svg/aot.svg

參考:

1. http://ryanarn.blogspot.tw/2011/04/using-googles-app-engine-to-serve-svg.html