在unix上編輯東西當然少不了vim。雖然沒有視窗的直覺化,但是透過簡潔的指令,我們更能專注於我們要編輯的內容,而不是隨意的剪剪貼貼。
下面介紹編輯檔案好用的多檔編輯:
1. 開啟檔案時可以 vim file1 file2 ... 一次開啟好多檔案。
2. 輸入 :files 會出現檔案名稱以及檔案編號。
3. :n 可以編輯下一個檔案
4. :N 可以編輯上一個檔案
5. :b# #代表輸入 :files 以後出現的檔案前面的編號,用來跳到編號為#的檔案編輯。
6. 按 Ctrl + ^ ,可以切換目前檔案以及前一個編輯的檔案。
透過這些指令在多個檔案的編輯也可以很簡單達成。
其他詳細的 vim 介紹,可以到鳥哥的 Linux 私房菜 - vi 文書處理器或是大家來學 Vim 一個歷久彌新的編輯器找到詳細內容。
參考資料 :
鳥哥的 Linux 私房菜 - vi 文書處理器 - http://linux.vbird.org/linux_basic/0310vi.php
大家來學 Vim 一個歷久彌新的編輯器 - http://edt1023.sayya.org/vim/
2008年6月10日 星期二
| [+/-] |
vim我會用的指令 |
2008年6月9日 星期一
| [+/-] |
Unix Socket程式範例 |
Server :
建立socket -> 設定local address -> 將socket bind在local port上 -> listen client socket -> 當有socket連線時accept client socket -> recv/send
Client :
建立socket -> 設定remote(server) address -> connect遠端server -> 當connect成功之後 -> recv/send
範例請見參考資料。
參考資料 :
http://www.pcs.cnu.edu/~dgame/sockets/sockets.html
2008年6月5日 星期四
| [+/-] |
read function 設定Non-Blocking I/O的方式 |
利用 fcntl 或是 ioctl 來設定 Non-Blocking I/O,讓 read function 不會被 Block住:
#include <fcntl.h> //fcntl的header
#include <sys/ioctl.h> //ioctl的header
...
int flag;
//用fcntl設定Non-Blocking I/O
fcntl(STDIN_FILENO, F_SETFL, O_NONBLOCK);
//用ioctl設定Non-Blocking I/O
ioctl(STDIN_FILENO, FIONBIO, &flag);
fcntl的完整用法是先用F_GETFL取得flags,在把flags和要改變的flag進行&、|,最後在用F_SETFL把flags設定回去。
int flags;
//取得flags
if ((flags = fcntl(fd, F_GETFL, 0)) == -1)
{
//進行錯誤處理
}
//加入non-blocking I/O的flag
flags |= O_NONBLOCK;
//設定flags
if ((flags = fcntl(fd, F_SETFL, flags)) == -1)
{
//進行錯誤處理
}
參考資料 :
Advanced Programming in the Unix Environment
| [+/-] |
C程式在Unix下取得時間 |
在Unix下取得時間的方法如下:
1. time取得系統時間 (time_t)
2. 把 time_t 的時間轉換成 struct tm 格式的時間:localtime (取得在地時間),gmtime (取得格林威治時間)
3. 用印出 struct tm 的函式來把時間用想要的格式印出:asctime (預設的格式),strftime (可以自訂輸出格式)
以上就是轉換流程,如果要把 struct tm 轉回 time_t 則是用 mktime 。
#include <time.h>
...
time_t t;
struct tm *tm_t;
//取得系統時間
t = time(NULL);
//把系統時間轉成當地時間
tm_t = localtime(&t);
//印出時間
printf("%s", asctime(tm_t));
//把strcut tm轉成time_t
//t = mktime(tm_t);
參考資料 :
http://www.cs.nctu.edu.tw/~yslin/library/linuxc/function/04.html
Standard C Date & Time - http://www.cppreference.com/stddate/index.html
| [+/-] |
關於匈牙利命名法的討論 |
進入公司後看到的 Code 大部分都是用 匈牙利命名法 這個規則命名的。一開始的時候看到變數前面有m_、n、sz、lp等等一連串的怪怪 prefix ,讓本來就不懂的程式碼看起來更加痛苦。但是慢慢的了解了這個規則以後,發現到看別人的程式,如果有這樣的命名其實還不錯。
像是大家常常喜歡執行一個 Member Function 來改變 Member Variables ,當然程式如果是自己寫的很容易就知道那些 Member Variables 被修改了。但是這可苦了看你程式的人,尤其是沒有寫 Function 註解的時候。往往一個 Member Function 做完了以後,沒有回傳值,根本不曉得它到底做了些什麼。
所以說 匈牙利命名法 真的這麼好嗎?
在網路上搜尋的結果,發現到當然也是有人覺得它不錯,但是下面這篇討論,指出了它受到質疑的缺點 : 匈牙利命名法討論的文章。
當然缺點裡面有誇大的地方,不見得所有的地方都是不好的。Making Wrong Code Look Wrong(中文)這篇文章,就指出了 匈牙利命名法 最初的原意。它的用途在於知道變數的種類(Kind),而不是型態(Type)。知道變數的種類(Kind)就能幫助看程式的人,從變數本身看出程式碼的錯誤。文章裡面也有舉了一個例子來說明。
所以當你使用 匈牙利命名法 來命名的時候,可以想想看這樣的做法是否可以幫助你了解程式,以及對程式的除錯是否有幫助。不然這樣的做法會讓程式的變數看起來眼花撩亂,可能跟你原本想要的結果大有不同 (最終目的是幫助程式的閱讀)。
參考資料 :發佈文章
Making Wrong Code Look Wrong(英文) - http://www.joelonsoftware.com/articles/Wrong.html
Making Wrong Code Look Wrong(中文) - http://chinesetrad.joelonsoftware.com/Articles/Wrong.html
匈牙利命名法討論的文章 - http://tsjeremy.spaces.live.com/blog/cns!BD4EB7F462639B16!1346.entry
| [+/-] |
程式碼的風格 (Coding Style) |
寫了這麼久的程式,但是對 Coding Style 卻沒有一個很明確的規範。常常變數寫怎麼取就怎麼取,因為以前寫的程式都是為了單一的目的 (通常是交作業) 而寫的。在進公司接了別人的程式以後,就深深的覺得好的 Coding Style 真的很重要。
像是變數的命名方式,如果在取名的時候不在 Class Member 前面加上 m_ 的前置符號,在看新的程式就會很難判別一個變數的範圍。往往在一個 Function 執行完以後,還不知道這個 Function到底做了些什麼事情,因為不曉得哪些變數是 Class Member 。帶大量參數的縮排也是一樣,好的縮排可以幫助我們很快的了解這個 Function 裡面的參數意義。當然我覺得最重要的還是在 Function 或是 Class 的前面可以加上註解會是更好的,常常你的小小註解,可以省下其他人 (更常是自己)很多時間。
下面這三份資料是介紹一些 Coding Style 的方法,分別是 C , C++ , C# 。但是我想基本概念都是差不多的。
C♯ Coding Style Guide
C++ Programming Style Guidelines
Recommended C Style and Coding Standards
希望大家的程式碼都是能容易閱讀的,讓看別人的程式碼變成是程式技術增進的快速方法,而不是一個痛苦的工作。
參考資料 :
C♯ Coding Style Guide - http://www.icsharpcode.net/TechNotes/SharpDevelopCodingStyle03.pdf
C++ Programming Style Guidelines - http://geosoft.no/development/cppstyle.html
Recommended C Style and Coding Standards - http://www.psgd.org/paul/docs/cstyle/cstyle.htm
2008年6月4日 星期三
| [+/-] |
Bsd裡面的ls顏色設定 |
設定的流程如下 :
1. 設定TERM這個環境變數為xterm-color (在openbsd裡面為xterm-color)
2. 確認 ls 產生顏色的方法。是用 ls --color 來設定,還是用 colorls -G 。
3. 在 .profile 檔裡面 (ksh) ,設定 alias 方便使用。例如:alias ls = 'colorls -G'
4. 更改顏色的設定,在bsd裡面是用LSCOLORS這個變數的設定
DIR=Dx
SYM_LINK=Gx
SOCKET=Fx
PIPE=dx
EXE=Cx
BLOCK_SP=Dx
CHAR_SP=Dx
EXE_SUID=hb
EXE_GUID=ad
DIR_STICKY=Ex
DIR_WO_STICKY=Ex
export LSCOLORS="$DIR$SYM_LINK$SOCKET$PIPE$EXE$BLOCK_SP$CHAR_SP
$EXE_SUID$EXE_GUID$DIR_STICKY$DIR_WO_STICKY"
上面的LSCOLORS後面的內容為一行。
詳細的介紹可以參考ls 顏色設定(in Bash shell)。
參考資料 :
ls 顏色設定(in Bash shell) - http://plog.longwin.com.tw/post/1/408
| [+/-] |
使用CAsyncSocket碰到的問題 : Create |
在用MFC寫網路程式的時候,會用CAsyncSocket來產生出自己的非同步Socket Class。用CAsyncSocket建立連線的時候,Client端只要呼叫Create和Connect這兩個Function就好了,不需要像純粹的Socket一堆設定動作。
而這些繁雜的設定都被包在Create裡面了,這也是我這次碰到的問題。
那Create裡面到底做了些什麼?
1. 呼叫CAsyncSocket的Socket函式
1.1 Socket函式裡面會先檢查m_hSocket是否被清成INVALID_SOCKET
1.2 呼叫利用socket函式產生新的m_hSocket
2. 如果呼叫Socket函式成功,接下來呼叫Bind函式,在Bind函式裡面會設定一些Socket相關資訊,然後進行Bind的動作
3. 最後就是成功Create,然後執行Connect到Server。
而我產生錯誤的地方就在 1.1 的部分,因為我的程式可能因為遠端Server無法連線而造成Connect失敗,在Connect失敗以後我沒有呼叫Close來清除m_hSocket的資源,並且將m_hSocket給設定成INVALID_SOCKET,因此在呼叫Create的時候就會產生出ASSERT的錯誤訊息。
所以對於使用的函式庫的function,如果能多了解裡面程式碼實際的內容的話,就能避免掉一些因為和函式庫設計邏輯不同而產生的錯誤。
參考資料 :
MSDN
2008年6月3日 星期二
| [+/-] |
libevent : event-driven的IO函式庫 |
網路IO最麻煩的就是多個連線的處理, 要用fork, thread, 或是select, poll之類的方式來處理.
libevent提供了很方便的方式來處理多個連線的問題. 你只要註冊某個file descriptor (socket), 在發生read event時做什麼動作, write event時做什麼動作就可以了. libevent會根據你所在的平台提供的解決方式來幫你選擇.
void callback(int fd, short event, void *arg); //callback function的prototype
struct event ev;
...
event_init(); //初始化
...
//設定fd在發生EV_READ時, 呼叫callback這個function
event_set(&ev, fd, EV_READ | EV_PERSIST, callback, &ev);
event_add(&ev, NULL); //加入event, 沒有設定timeout
在event_set裡面的EV_PERSIST代表執行過callback function之後, 不要把這個event移除. 如果沒有設定這個flag的話, 每次執行過callback function, 就需要event_add一次, 不然這個event就不會作用.
參考資料 :
libevent - http://monkey.org/~provos/libevent/
| [+/-] |
讓vim可以有變色的效果 |
在.vimrc (vim的設定檔)裡面加入
syntax on這一行, 在用vim時有程式語法的地方就會變色.
如果這樣還是不行, 就去shell的設定檔裡面 (在csh, tcsh裡面是.cshrc, 在ksh和sh裡面是.profile),
在.profile裡面加入export TERM=xterm-color
在.cshrc裡面加入setenv TERM xterm-color
如此在用vim開啟檔案的時候, 就會出現彩色的變色了.
P.S. 記得設定完.profile或是.cshrc檔以後, 要重新login, 這樣設定檔才會被重新讀取.
參考資料 :
Tsung's Blog - http://plog.longwin.com.tw/post/1/369
http://www.chu.edu.tw/~chunpo/solaris/user/docs/shell.html
2008年6月2日 星期一
| [+/-] |
Pygame : 人物跟著滑鼠旋轉 |
在遊戲裡面常常會用滑鼠來控制角色的移動, 而滑鼠目前的位置和人物的連線會被當作是人物正面朝向的方向. 這裡簡單的用python裡面的math module來計算人物和滑鼠位置的夾角.
首先介紹三角函數的arc function :
atan(x) : x 代表弧度(radians)
atan2(y, x) : 計算atan(y/x)的弧度, 回傳的值在-pi和pi之間. 因為有給(x,y)的座標, 所以能判斷座 標是在那個象限裡面, 也就能知道正確的方位.
radians(x) : 把角度(degree)轉成弧度(radian)
degrees(x) : 把弧度轉成角度
因為人物移動的時候也可以用鍵盤做細微的移動, 所以角度的儲存是以degree方式儲存.
所以要讓人物面向滑鼠目前方向的角度為 :
rotate = math.degrees(math.atan2(x, y))
參考資料 :
http://docs.python.org/lib/module-math.html
2008年5月30日 星期五
| [+/-] |
Pygame給新手的建議 |
下面內容是參考A Newbie Guide to pygame的一些重點節錄 :
1. 了解什麼是Surface, 以及一些Surface重要的functions
pygame.display.set_mode : 建立一個代表螢幕的Surface, 而且只能建立一個 (SDL的限制)
image.load : 讀取一個圖片, 並且將它建立成Surface
font.render() : 把一段文字建立成Surface
blit : 在螢幕的Surface上面, 畫上其他Surface
fill, set_at, get_at, ...
2. 當用image.load讀取圖片的時候, 利用convert將圖片進行轉換, 以加速執行.
ex : my_surface = pygame.image.load("my_pic.bmp").convert()
3. 畫完Surface記得要呼叫pygame.display.update()來更新畫面.
4. 使用pygame裡面的Rect資料型態來表示一個物件, 因為Rect裡面有很多function能幫助你處理
物件的碰撞
5. 檢查碰撞時, 可以用比原本物件小的Rect來幫助加速判斷
6. event的處理有分成兩種 :
1. state system : 利用pygame.mouse.get_pos()和pygame.key.get_pressed(), 取得目 前發生的event
優點 : 檢查同時按下容易, 準確的知道目前的event為何
缺點 : 如果在呼叫function之前有event產生又結束, 則這個event會lost
2. SDL event queue : event.poll(), event.wait()
優,缺點和state system相反
至於使用時機則是看應用而定, 如果呼叫event function的delay很短, 使用state system會比較方便. 如果不在乎輸入有delay, event產生的完整性比較重要的情形 (ex : 使用者輸入字串), 則適用SDL event queue.
7. Colorkey : 設定Colorkey之後, 可以讓Surface裡面的某個顏色不被畫出.
ex : surface.set_colorkey((0,0,0)), 會把surface裡面的黑色(0,0,0)不畫出, 而呈現背景的顏色.
8. Alpha : 設定Surface的半透明程度, 數值越大代表接近背景的比例越大, 如果數值為255, 則會呈 現完全透明.
參考資料 :
http://www.pygame.org/docs/tut/newbieguide.html
2008年5月29日 星期四
| [+/-] |
寫註解的13個建議 |
在看別人的code時候, 往往因為文件不清楚, 或是註解不明確, 造成要了解程式必須要慢慢的去推敲過程. 這是非常浪費時間的事情, 因為往往只要知道程式流程, 就能掌握程式的精神. 對於細節的部分是不需要太去了解的, 除非程式存在bug.
所以寫好的註解可以幫助提升看程式的效率, 大多時候一個程式不會是從無到有. 而是會建立在別人的程式之上, 所以看程式的效率提升了, 寫程式的效率自然也會提升.
13 Tips to Comment Your Code這篇文章裡面就介紹了建議如何寫好的注解的方法.
除了註解以外, 文件也是很重要的. 除了大方向的程式運作之外, functions之間的運作流程也是幫助閱讀程式的好幫手. 尤其是寫event-driven的視窗程式, 如果能有各個功能的function流程, 能夠很快的幫助看程式的人了解程式運作, 而不需要自己去摸索猜測.
參考資料 :
13 Tips to Comment Your Code
http://www.cnblogs.com/oomusou/archive/2008/04/26/1172208.html
2008年5月28日 星期三
| [+/-] |
MFC中的List Control如何刪除Item |
在List Control裡面要移除一個想要刪除的Item
- 可以在加入LVITEM Item時, 設定lParam這個變數, 在找尋Item的時候就可以用GetItemData取出這個Item的IParam值, 根據IParam來判斷是否為想要刪除的Item.
- 如果Item裡面有獨一無二的值, 也可以直接取出pszText來判斷. 利用GetItemText(Item_num, SubItem_num)來取得. SubItem_num是指在這個Item裡面的第幾個欄位.
for (int nItem = 0; nItem < m_lServerList.GetItemCount(); )
{
// if (m_lServerList.GetItemState(nItem, LVIS_SELECTED) == LVIS_SELECTED)
// m_lServerList.DeleteItem(nItem);
CString text;
if(text = m_lServerList.GetItemText(nItem,0) == "Delete Item")
m_lServerList.DeleteItem(nItem);
else
++nItem;
}
上面的code是先利用GetItemCount取得這個List Control總共有多少Item. 再重頭走過這些Item, 取出這些Item的第一個欄位(欄位0), 如果欄位的值為"Delete Item"這個字串的話, 就把這個Item刪除.
要注意的地方在於++nItem只有在沒有執行刪除的時候做, 因為如果進行刪除動作以後, 所有的Item都會被往後移動一個位子, 因此不需要++nItem.
而註解的地方則是如果一想要在這個Item被選取的時候刪除的做法.
參考資料 :
http://www.codeguru.com/forum/showthread.php?t=388818
http://hi.baidu.com/lufa2014/blog/item/0a5c069bbc1017b0c9eaf4fd.html
| [+/-] |
改變MFC中CFormView的視窗式樣, 以及改變視窗標題 |
利用MFC Wizard建立一個MFC專案, 並且選擇產生CFrameWnd的模式, MFC Wizard會幫你產生以下檔案 :
- 繼承CWinApp的檔案 (專案名稱.cpp .h)
- 繼承CDocument的檔案(專案名稱Doc.cpp .h)
- 繼承CFormView or CView的檔案(專案名稱View.cpp .h)
- 繼承CFrameWnd的檔案 (MainFrm.cpp MainFrm.h)
- stdafx.cpp stdafx.h
- 資源檔
在CWinApp裡面會根據你選擇是SDI(Single Document Interface)或是MDI(Multiple Document Interface)產生控制Doc, View和MainFrm的元件.
因為控制主框架(視窗)是由MainFrm所控制的, 所以改變視窗式樣也是在這裡面改變.
在MainFrm.cpp裡面的PreCreateWindow(CREATESTRUCT& cs), cs這個變數就是用來改變視窗式樣的變數. 它是由多個控制視窗的flag所組成的.
例如 :
cs.style = WS_OVERLAPPED | WS_CAPTION | FWS_ADDTOTITLE
| WS_THICKFRAME | WS_MINIMIZEBOX | WS_SYSMENU;
由這樣的方式就能控制視窗式樣.
改變視窗的標題則是在View裡面取得Doc的變數然後進行設定 :
CServerMonitorDoc* pMyDoc = GetDocument();
pMyDoc->SetTitle("My Application Name");
其中的CServerMonitorDoc是看你Doc的class名稱為何.
參考資料 :
http://www.microsoft.com/msj/0198/c0198.aspx
2008年5月27日 星期二
| [+/-] |
IP和Host Name的轉換 |
在設計網路連線的程式裡面, 常常會需要輸入ip或是host name. 設計良好的程式應該是要能接受這兩種不同的格式, 然後自行轉換.
下面就介紹如何轉換ip到host name以及host name到ip.
//----------------------
// Declare and initialize variables
hostent* remoteHost;
char* host_name;
unsigned int addr;
//----------------------
// User inputs name of host
printf("Input name of host: ");
host_name = (char*) malloc(sizeof(char*)*16);
fgets(host_name, 16, stdin);
// If the user input is an alpha name for the host, use gethostbyname()
// If not, get host by addr (assume IPv4)
if (isalpha(host_name[0])) { /* host address is a name */
host_name[strlen(host_name)-1] = '\0'; /* NULL TERMINATED */
remoteHost = gethostbyname(host_name);
}
else {
addr = inet_addr(host_name);
remoteHost = gethostbyaddr((char *) &addr, 4, AF_INET);
}
上面的程式碼主要功能在於輸入host name轉成ip, 或是輸入ip轉成host name.
if (isalpha(host_name[0])) { /* host address is a name */
host_name[strlen(host_name)-1] = '\0'; /* NULL TERMINATED */
remoteHost = gethostbyname(host_name);
}
這一段是先判斷輸入的內容是否有英文字母, 如果是英文字母則判斷為host name.
下面就用gethostbyname進行轉換.
else {
addr = inet_addr(host_name);
remoteHost = gethostbyaddr((char *) &addr, 4, AF_INET);
}
而這一段是判斷為ip address, 先將ip address用inet_addr轉成internal address形式,
再用gethostbyaddr取得host資訊.
取得host資訊以後, 再以remoteHost->h_name取得host name或是ip address.
如此一來就能在使用者輸入ip或是host name時同時取得兩種資訊.
參考資料 :
MSDN
2008年5月26日 星期一
| [+/-] |
wxPython自訂event |
下面介紹如何自訂一個自己的event :
class TwoButtonEvent(wx.PyCommandEvent): #自訂event
def __init__(self, evtType, id):
wx.PyCommandEvent.__init__(self, evtType, id)
self.clickCount = 0
def GetClickCount(self):
return self.clickCount
def SetClickCount(self, count):
self.clickCount = count
myEVT_TWO_BUTTON = wx.NewEventType() #建立新event
EVT_TWO_BUTTON = wx.PyEventBinder(myEVT_TWO_BUTTON, 1) #建立新event binder
上面的部分為定義event和event binder. 在要產生event的地方則是以下面的code來產生event :
evt = TwoButtonEvent(myEVT_TWO_BUTTON, self.GetId())
evt.SetClickCount(self.clickCount)
self.GetEventHandler().ProcessEvent(evt)
利用ProcessEvent來將event產生到queue裡面等待處理.
參考資料 :
http://www.pythontik.com/blog/article.asp?id=192
| [+/-] |
wxPython的Event |
要讓widget元件根據使用者的行為做不同的反應, 就需要建立event. event的功能在於使用者所做的行為: 滑鼠點擊, 鍵盤輸入, ... 以及其他視窗狀態要改變時, 通知視窗做改變.
主要流程可以簡單看成以下幾步 :
- 對每個widget元件榜定某個event發生時所要執行的functino(例如:button在按下時執行某個function, self.Bind(wx.EVT_BUTTON, self.OnClick, function) )
- 程式開始執行時, 在視窗產生以後, 會執行一個無限的迴圈. 在迴圈裡面會檢查事件是否發生, 如果發生就去執行對應的function
- 一直到程式結束的event發生才會離開無限迴圈, 進而結束程式
例如滑鼠的event(wx.MouseEvent)它的binder有以下這些 :
wx.EVT_LEFT_DOWN
wx.EVT_LEFT_UP
wx.EVT_LEFT_DCLICK
wx.EVT_MIDDLE_DOWN
wx.EVT_MIDDLE_UP
wx.EVT_MIDDLE_DCLICK
wx.EVT_RIGHT_DOWN
wx.EVT_RIGHT_UP
wx.EVT_RIGHT_DCLICK
這些分別是各種不同滑鼠event的event binder. 如果要榜定所有的滑鼠event執行同個function, 則可以用wx.EVT_MOUSE_EVENTS.
還有一些比較常用的event binder, 像是button被按下(wx.EVT_BUTTON), menu被按下(wx.EVT_MENU)等等.
而榜定event的方式有兩種 :
- wx.EvtHandler裡面的Bind function, 所有的widget都有繼承wx.EvtHandler, 所以都有Bind function可以使用.
Bind(event, handler, source=None, id=wx.ID_ANY, id2=wx.ID_ANY)
例子 : self.Bind(wx.EVT_BUTTON, self.OnClick, button) - 把event binder當成函數傳入參數使用.
例子 : wx.EVT_BUTTON(self, self.button.GetId(), self.OnClick)
- AddPendingEvent(event):把event加入處理的queue中, 不會馬上處理, 用於非同步的時候 使用
- Bind(event, handler, source=None, id=wx.ID_ANY, id2=wx.ID_ANY) : 把event和binder綁在一起
- GetEvtHandlerEnabled()
- SetEvtHandlerEnabled( boolean)
- ProcessEvent(event):把event加入處理的queue, 並且馬上處理
參考資料 :
http://www.pythontik.com/blog/article.asp?id=188
http://www.pythontik.com/blog/article.asp?id=189
2008年5月23日 星期五
| [+/-] |
在MFC裡讓元件隨著視窗改變大小 |
使用視窗的時候, 我們常常會去改變視窗的大小. 而應用程式在視窗改變大小的同時, 也會把顯示在視窗的元件隨著視窗而改變. 在wxWidget裡面, 我們可以把元件加入Sizer來達到這樣的效果. 可是在MFC中就沒這樣的預設元件可以使用, 我們必須在視窗改變大小的時候(WM_SIZE)去自行重繪.
下面介紹如何簡單的讓一個List Control不論視窗如何改變都能和視窗保持伊個邊界的方法.
- 在你主要視窗的Class裡面加入一個WM_SIZE觸發時可以執行的function OnSize (名稱可以自訂, 但是這樣的名子比較具有可讀性)
- 在視窗改變大小時就會去呼叫OnSize, 這時候會先取得視窗目前的大小 (GetClientRect)
- 取得要變更大小的元件 (GetDlgItem), 並且檢查是否取得成功
- 計算新位置
- 移動視窗到新位置並且重繪 (MoveWindow, RedrawWindow)
void CServerMonitorView::OnSize(UINT nType, int cx, int cy)
{
CFormView::OnSize(nType, cx, cy);
int TopLeft_x = 10;
int TopLeft_y = 25;
// Declare a CRect to get the co-ordinates
CRect l_formRect;
GetClientRect(&l_formRect);
// get pointer to the control to be resized dynamically
CListCtrl* pListCtrl;
pListCtrl = (CListCtrl *)GetDlgItem(IDC_LIST1);
if(!pListCtrl)
return ;
//Calculate the Width and TopLeft position of the control to be resized
long newX = l_formRect.TopLeft().x + TopLeft_x;
long newY = l_formRect.TopLeft().y + TopLeft_y;
long newWidth = l_formRect.Width() - 10 - TopLeft_x;
long newHeight = l_formRect.Height() - 10 - TopLeft_y;
// Now resize the control dynamically by calling MoveWindow
pListCtrl->MoveWindow((long)newX, (long)newY, (long)newWidth, (long)newHeight, TRUE);
// repaint control
pListCtrl->RedrawWindow();
}
TopLeft_x和TopLeft_y是設定List Control元件的top left位置.
這只是簡單的Resize設定, 在有多個元件的時候重新設定位置會比較複雜. 不過好處是你可以決定要怎麼改變你的元件, 比較有彈性 (麻煩?).
參考資料 :
http://www.codersource.net/mfc_resize_controls.html
2008年5月22日 星期四
| [+/-] |
blogger裡面的tag |
在blogger裡面有自己的tag語法來做版面設定, 在Widget Tag for Layout裡面有詳細的說明.
雖然不用完全了解它的意義, 但是為了要使用別人的排版元件, 還是要知道如何去加在自己的版面設定裡面. 像是在文章顯示的時候, 要以什麼樣的排版顯示, 要顯示哪些資訊等等, 都可以自己設定.
主要的版面設定的tag是 <div class="'blog-posts">, 在底下的code就是決定在顯示文章時如何做排版.
有兩個常常會看到的tag, 一個是include, 一個是includable.
- include :
<b:include data="'include_var'" name="'includable_id'/">
include的用途在於使用一個被定義好的includable tag. 把name填入includable的id, data則是要傳入includable的var裡面的變數.
用這樣的方式來呼叫includable, 就可以利用定義好的元件來產生想要的版面 - includable :
<b:includable id="'main'" var="'var'"></b:includable>
...
</b:includable>
定義一串版面排版, 再利用include來呼叫, 用來讓版面的設計更容易閱讀.
data tag :
用來產生資料的tag, 可以用blogger裡面的一些global data來使用. Layouts Data Tags介紹data tag要如何使用, 以及一些global的data像是 : blog, posts, labels, ... 利用這些global data就能讓我們自由的對資料做排版.
參考資料 :
海芋小站 - http://inote.tw/search/label/Blogger
Widget Tag for Layout - http://help.blogger.com/bin/answer.py?answer=46995&hlrm=en
Fun New Run High - http://skyvee.net/