Gtk# UI設計隨便聊

前一陣子用MonoDevelop來寫 Gtk# 應用程式玩,發現真的是很簡單。
幾乎可以跟 SharpDevelop 或 Visual Studio.Net 設計 Windows form 應用程式一樣了,同樣也是拖拉放,就可以完成。
對於初入門的人來說,最痛苦的可能還是對於 Gtk# 的物件模型不熟悉,因為不熟悉,所以也就不知道該去處理哪些屬性與事件。
好在Mono官方網站有提供入門文件:

用力唸一唸這些文件跟自己去嘗試,大致上應該是夠了。但這些文件大部分都是自己去寫 Layout code。
如果你正在使用目前版本的 MonoDevelop 在 Layout 的話,你會發現世界全然不同,因為MonoDevelop引進了 Stetic
現在使用 MonoDevelop 拉出來的 Form,實際上都會被存放在專案目錄下的 gtk-gui 目錄下的某個 xml 檔案,在每次 build 之前,MonoDevelop 會先把 xml 轉換為一個存放於 gtk-gui 目錄下的 partial 類別(.cs),然後 build 的時候,再 build 到一起。而在專案目錄下所看到的代碼,只會有一行 Build()。
這真的是方便很多,開發者只需要專注於如何處理程式邏輯就可以,而不需要去管 UI 代碼的生成。
MonoDevelop 加入 Stetic 之前,大部分的 Gtk# 應用程式還是用手動加代碼或是用 Glade# 來設計介面(詳細的 Glade# 用法,可以參考 Your First Glade# Application 的說明)。
Glade# 其實也是把整個 UI 存成一個 xml 檔案,所以你得先使用 Glade 應用程式設計出 UI,得到 .glade 檔案,然後在程式裡面:

  1. 載入 .glade 檔案
  2. 為控制項(Widget)加上 Attribute
  3. 撰寫事件處理並繫結


這當然是比手動寫代碼方便許多,但比起 Stetic,還是要多作事,往往用 Glade 修改 UI 後,可能會忘記加必要的 Widget 宣告,或是對應的事件繫結。

Gtk.TreeView(3)

在 Linux 裡面,所謂的”事件”,多半是用 Signal 來表示,所以你看到 Signal 時,可以概略地當作”事件”來看。
而這些事件跟 Windows Form 的事件命名法差異相當大。
以下這些是TreeView比較常用到的:

    protected virtual void OnTreeview1RowActivated (object o, Gtk.RowActivatedArgs args)
{
// double click
Console.WriteLine( String.Format("[{0}]: {1}", o.GetType().ToString(), "row activated") );
}
protected virtual void OnTreeview1RowCollapsed (object o, Gtk.RowCollapsedArgs args)
{
// collapse
Console.WriteLine( String.Format("[{0}]: {1}", o.GetType().ToString(), "row collapsed") );
}
protected virtual void OnTreeview1RowExpanded (object o, Gtk.RowExpandedArgs args)
{
// expand
Console.WriteLine( String.Format("[{0}]: {1}", o.GetType().ToString(), "row expanded") );
}
protected virtual void OnTreeview1CursorChanged (object sender, System.EventArgs e)
{
// click
Console.WriteLine( String.Format("[{0}]: {1}", sender.GetType().ToString(), "Cursor changed") );
}

我是怎麼查到的呢?
坦白說,我是用笨方法,看到那些 Console.WriteLine 了沒?
我先試著在可能的事件裡面放置這些 Console.WriteLine,接著執行程式,試著去 Click、Expand,然後看 Console 輸出就知道了…

電影流水帳(2007/11/29~2007/12/13)

只看了兩部片。不知道今年我到底看了多少片子?到年底的時候來算算看。

  • Transformer(Wikipedia,IMDB),中譯:變形金剛,受到台灣很多人喜愛的片子,特效炫,故事緊湊,女主角也很正,完全符合好萊塢作風,也難怪會賣座。個人唯一覺得不好的地方就是,金剛們的金屬味很重,沒有卡通裡面那種色彩強烈的感覺。
  • Silent Hill(Wikipedia,IMDB),中譯:沉默之丘。第一次挑戰英文字幕,除了很多單字以外,其實看來不會很吃力。整個裡面的氣氛鋪陳的很好,片中等於有兩個世界,兩個世界交錯地發生事情,不會讓人有搞不懂的情況。總之,我覺得拍的不錯。不過片尾並沒有所謂的結局,看來應該是有打算要拍續集,希望可以看到最後這兩個世界是怎麼再次接軌起來。(謎之聲:世界末日與冷酷異境書末最後兩個世界也沒接起來啊,你要期待什麼?)

Gtk.TreeView (2)

根據昨天的程式,稍作改良,就可以把整個目錄樹丟進去顯示了…
下面就是利用 Recursive 來把資料塞到 TreeStore 裡面去。

public void _buildTreeStore( TreeStore store, DirectoryInfo di, TreeIter parent )
{
foreach( DirectoryInfo iter in di.GetDirectories() )
{
TreeIter ti = store.AppendValues( parent, iter.FullName, iter.Name, "" );
_buildTreeStore( store, iter, ti, true  );
}
// add files in tree
foreach( FileInfo fi in di.GetFiles() )
store.AppendValues( parent, fi.FullName, fi.Name, fi.Length.ToString() );
}
public TreeStore buildTreeStore( string path )
{
// 0: fullpath 1: name 2: filesize
TreeStore store = new TreeStore( typeof(string), typeof(string), typeof(string) );
DirectoryInfo di = new DirectoryInfo( path );
TreeIter root = store.AppendValues( path, di.Name, "" );
_buildTreeStore( store, di, root );
return store;
}
public ExplorerSharp(): base(Gtk.WindowType.Toplevel)
{
Build();
TreeStore store = buildTreeStore( "./" );
// 設定資料來源
treeview1.Model = store;
// 不顯示表頭
treeview1.HeadersVisible = false;
// 設定要顯示的欄位
treeview1.AppendColumn("Name", new CellRendererText(), "Name", 1 );
treeview1.AppendColumn("Size", new CellRendererText(), "FileSize", 2 );
// 一次只能選定一列或一個節點
treeview1.Selection.Mode = Gtk.SelectionMode.Single;
}

Gtk.TreeView (1)

使用 TreeView 的基本,首先就是添加列與設定要顯示的欄。
呈現的方式,取決於你給 TreeView.Model 屬性的資料是什麼,如果是 TreeStore,就會顯示 Tree,如果是 ListStore,就會顯示 Grid。

// 如果你把 TreeStore 改為 ListStore,就能得到類似 Grid 的效果。
TreeStore store = new TreeStore();
for( int i=0; i<10; i++ )
{
TreeIter iter = store.AppendValues( "Demo " + i.ToString(), "" );
// 添加子節點,你可以把這兩行註解掉試試看,這樣就沒有子節點了。
for( int j=0; j<10; j++ )
store.AppendValues( iter, "", "Child " + j.ToString() );
}
// 設定資料來源
treeview1.Model = store;
// 不顯示表頭
treeview1.HeadersVisible = false;
// 設定要顯示的欄位
treeview1.AppendColumn("Name", new CellRendererText(), "i", 0 );
treeview1.AppendColumn("Size", new CellRendererText(), "j", 1 );
// 一次只能選定一列或一個節點
treeview1.Selection.Mode = Gtk.SelectionMode.Single;

正太與正太控

我老妹看到我親我兒子,就會這麼說我…
不過,現在不親的話,長大以後,大概就沒機會這樣親了吧~
007-正太與正太控?-02

Gtk.TreeView

這幾天玩MonoDevelop Stetic (gtk# 的 UI designer) 的時候,發現 gtk# 裡的 TreeView Widget 是個很有趣的元件。
有趣的原因,是因為他的設計哲學整個跟 Windows form 的設計哲學截然不同,Windows form 的 TreeView 就是很單純的 TreeView,沒別的。
而 gtk# 的 TreeView Widget 則是以所謂的 MVC Pattern 去設計,所以,其實 TreeView Widget 會根據你塞進去的 Store (Model) 來決定該怎麼去顯示。也因此,它既可以當作 Grid 來用,也可以當作 TreeView 來用,也可以當作有 TreeView 的 Grid 來用。
摸索的時候,花了不少時間,但今天打算寫這篇的時候,發現Mono官方網站就有 Tutorial:GtkSharp TreeView Tutorial,真的是白白浪費了摸索的時間…

“嘿~你那邊補的怎麼樣?”,正在低頭收拾東西的人說道。
“差不多啦~”,正在對著牆上塗抹著的人回答道。
“天色也晚了,該走啦~”
“好,等我一下。”
夕陽試著想拉出他們倆的影子,可是卻不能夠…


是嗎?世界末日與冷酷異境裡的牆不會壞,所以也不需要補是嗎?
白開頭了…

WHR-G54S

家裏用了 3.5 年的 WBR-G54 最近不知怎麼地都怪怪的,上星期五嚴重地罷工了,完全無法用 ADSL modem 撥號。
於是在週六決定改刷 Tomato Firmware試試看,結果…失敗…這台 WBR-G54 就壯烈成仁了。
當機立斷地請在外頭的大妹在回家時幫我帶一台無線 AP 回來,有鑑於之前使用 Buffalo 都還蠻順利的,就指定了同一牌子。
最後買回來就是這台WHR-G54S
目前一切順利,該設定的都設定好了,只差無線網路還沒有測試。