顯示進度 (wx.Gauge):wxPython in Action(五十六)
顯示進度 Dialog (wx.ProgressDialog):wxPython in Action(七十七)
分割視窗 (wx.SplitterWindow) : wx.SplitterWindow
多重版面 (wx.Notebook):wx.Notebook
Wizard (安裝導覽, 教學導覽) (wx.Wizard):wxPython in Action(八十一)
參考資料 :
The wxPython tutorial - http://zetcode.com/wxpython/
wxPyhthon In Action - http://www.pythontik.com/blog/default.asp
2008年6月17日 星期二
| [+/-] |
wxPython中有興趣的widget |
2008年6月16日 星期一
| [+/-] |
C : 非固定參數function |
有很多格式化的function,像是 printf,scanf 之類,由於格式不固定,造成後面接的參數個數也不一定。這時候就要用到非固定參數 function 的技巧了。
#include <stdarg>
void print(char *fmt, ...)
{
//宣告一個多參數變數
va_list ap;
//重組的格式
char *nFmt;
//取得多參數變數的初始化
va_start(ap, fmt);
//重組字串的格式 (asprintf 會自動幫buf allocate記憶體)
asprintf(&nFmt, "%s\n", fmt);
//輸出結果到stdout
vfprintf(stdout, nFmt, ap);
//釋放asprintf 自動 allocate的記憶體
free(nFmt);
}
這個function print所做的事情就是把原本要印出的字串格式重組,
最後在後面加個"\n"。
參考資料 :
man va_start
2008年6月13日 星期五
| [+/-] |
libpq : include 和 library 的設定 |
在 shell 下輸入:pg_config --help ,會出現很多 option 可以選擇。
其中 --includedir 是 libpq C header的位置
而 --libdir 是 library的位置
在compile時,把 gcc -I (include dir) 和 -L (library dir) 的 path 設定正確,這樣才不會出現 undeclare 或是 undefined reference 的錯誤產生
參考資料 :
http://www.freebsd.org.hk/html/postgresql/libpq-chapter.htm
| [+/-] |
libpq : 存取PostgreSQL的C API |
我們寫的程式透過 libpq 的 api 可以間接存取 PostgreSQL 裡面的資料。
要對 PostgreSQL 執行一個 SQL Command 步驟分成以下幾步:
1. 連上 PostgreSQL 上的 DB ( conn = PQconnectdb(connStr) )
2. 檢查 PQconnectdb 回傳的狀態 ( PQstatus(conn) == CONNECTION_OK )
3. 執行 SQL Command ( res = PQexec(conn, SQLCmd) )
4. 檢查回傳的 res 是否正常 ( PQresultStatus(res) == PGRES_TUPLES_OK )
5. 取出 res 裡的資料 ( for (i = 0; i < PQntuples(res); i++)
{
PQgetvalue(res, i, 0)
}
)
6. 清除資料 ( PQclear(res) )
7. 和資料庫斷線 (PQfinish(conn))
參考資料 :
C Language Interface (LIBPQ) - http://www.postgresql.org/files/documentation/books/aw_pgsql/node147.html
2008年6月12日 星期四
| [+/-] |
C++ : 如何把class member function當成function pointer傳入 |
將 member function 宣告成 static 就可以傳入。
可是 static member function 不能呼叫this pointer,這樣就失去呼叫 member function 的意義了。解決方法是把在呼叫時,把物件的指標傳入。但是呼叫別人的function時,不能改變參數,所以必須建立一個 global variable 或是 static member variable ,在產生物件的同時,把這個變數設定成物件的 reference ,這樣一來呼叫 static member function 時,就可以利用這個 variable 來呼叫物件的其他東西。
class Obj
{
public:
static void *self; //指向自己的pointer
Obj()
{
Obj::self = this; //指向自己
}
static void WrapperFun() //wrapper function
{
Obj::self->Fun(); //真正要呼叫的member function
}
void Fun()
{
//...
}
};
呼叫時:
some_function(WrapperFun);
這樣就可以達到呼叫我們要呼叫的non-static member function的效果了。
參考資料 :
Executing an Object's Member Function in a Separate Thread - http://gethelp.devx.com/techtips/cpp_pro/10min/10min0800.asp
How to use SetTimer() with callback to a non-static member function - http://www.cppblog.com/leo-chen/archive/2007/05/16/24186.html
2008年6月10日 星期二
| [+/-] |
vim我會用的指令 |
在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月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