QQ在許多公司內部被禁止使用,為了能使用QQ,稍微懂點兒計算機的人都知道用代理。QQ提供了socket和http代理這兩種功能,socket代理功能強大,但一般公司對外允許連接的端口號比較有限,難以利用。大多數公司是允許連接外部的80端口的,這樣使用QQ的http代理是可行的。但是找到能用的QQ代理有點兒麻煩,因此下面的Python代碼提供了自動進行QQ代理驗證的功能。
import urllib2
import socket
import re
f = urllib2.urlopen('http://www.proxycn.com/html_proxy/http-1.html')
content = f.read()
f.close()
ipPattern = re.compile(r'(\d+\.\d+\.\d+\.\d+):80')
ipList = ipPattern.findall(content)
print ipList
requestData = "CONNECT http.tencent.com:443 HTTP/1.1\x0d\x0a"
requestData += "Accept: */*\x0d\x0aContent-Type: text/html\x0d\x0a"
requestData += "Proxy-Connection: Keep-Alive\x0d\x0a"
requestData += "Content-length: 0\x0d\x0a\x0d\x0a"
for ip in ipList:
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
try:
s.connect((ip,80))
s.send(requestData)
data = s.recv(64)
if data.find("200 Connection established")!= -1:
print ip, 'good'
# A vailable proxy is found once, then exit the program
s.close
exit(0)
else:
print ip, 'bad'
except socket.error:
print ip, 'dead'
finally:
s.close
程序在找到一個可用的QQ代理后退出,用good標識。另兩種代理服務器的狀態是dead,說明本地無法連接到代理服務器,或是bad,能與代理服務器建立連接,但是代理不能與QQ服務器通訊。
代碼思路
通過代理中國獲取到80端口的代理服務器列表,使用了urllib2模塊獲取頁面數據,然后正則表達式解析出80端口的IP地址存入list中。接下來的幾行代碼簡單,但是很重要,使用較為底層的socket對象,構造合適的數據包通過代理,請求與QQ服務器連接,通過讀取的返回數據包來驗證連接是否能建立。
這里主要涉及到了HTTP協議的CONNECT的概念,很多人可能認為http代理只是為web瀏覽提供服務,其實CONNECT方法允許允許用戶建立TCP連接到任何端口,這意味著代理不僅可用于HTTP,還可用于FTP,QQ等其他協議。只是網上提供CONNECT方法的代理服務器比較少,我有時候掃了一大堆,也沒有找到一個可用的代理。反過來說,有時候你找到的能瀏覽網頁的http服務器,未必能用在QQ上,QQ需要的是能CONNECT的代理。網頁瀏覽一般只使用HTTP協議的GET或POST方法,提供這兩種方法的服務器就多了。
了解了代碼的原理,稍做改動,就可以用于其他類型的代理的驗證了,需要的是一些基本網絡知識和數據報的發送和接收。
posted @
2008-07-02 22:13 len 閱讀(1602) |
評論 (1) |
編輯 收藏
Subversion的屬性是非常好用的功能,它將一些工作自動化,實現為受版本控制的源文件添加元信息的作用。屬性是外部不可見的,可以簡單認為是附加上在文件上的信息,和文件大小之類的信息是一樣的,只不過他是通過subversion來管理的。屬性的名稱和值可以是你希望的任何值,限制就是名稱必須是可讀的文本,并且最好的一點是這些屬性也是版本化的,就像你的文本文件內容,你可以像提交文本修改一樣修改、提交和恢復屬性修改,當你更新時也會接收到別人的屬性修改—你不必為適應屬性改變你的工作流程。
Subversion保留了一組名稱以svn:
開頭的屬性,來預定義一些有用的功能。比如你常會看到一些人的源代碼底部有像下面之類標識的文字:
$Id: main_window.py 68 2008-06-30 02:05:05Z Len $
這就使用了Subversion 中的 svn:keywords的自動屬性,它讓將發生在源代碼中的一些屬性的變化自動地更新到源代碼中。這行字的意思是表示,main_windows.py 這個源代碼文件最后被用戶 len 更新于 2008-6-30 02:05:05Z,修訂版本號為 68。要實現這樣的自動更新,你只要對需要這樣屬性的文件上使用下面這行指令。
> svn propset svn:keywords "Id" main_window.py
或者使用TortoiseSVN中的Properties的操作按鈕,方便地增加新的屬性。接著需要在源代碼文件中需要 Subversion 進行自動更新的地方插入 $Id$ 這樣的 Keyword,那么在你下次進行提交更新時,該$Id$
就會被 Subversion 自動替換為$Id: main_window.py 68 2008-06-30 02:05:05Z Len $
這樣的格式。
Subversion 中可以使用的Keyword 包括下面這些:
- Id
上面介紹過的綜合的格式
- LastChangedDate
最后被修改的時間,縮寫為 Date。
- LastChangedBy
最后修改該源代碼文件的用戶名,縮寫為 Author。
- LastChangedRevision
最后修訂的版本號,縮寫為 Revision。
如果想每次向Subversion服務器提交文件修改時,都要設置文件的屬性,則需要進行Subversion配置的修改。配置文件在你用戶的主目錄下,在Windows下應類似于C:\Documents and Settings\Len\Application Data\Subversion\config
文件,Len是Windows用戶名,注意Application Data是隱藏文件夾,需要顯示全部文件才能看到。接著如下相應的修改,對你想要處理的文件做配置。
enable-auto-props = yes
[auto-props]
*.c = svn:keywords=Id
*.py = svn:keywords=Id
對于開源項目,常見其源文件頭部有著版權聲明的文本,這些操作大多也是通Subversion的屬性功能來完成的,有關更詳細的介紹和操作指南,可參見Subversion中文手冊中的屬性章節。
posted @
2008-06-30 12:06 len 閱讀(1861) |
評論 (0) |
編輯 收藏
這個類表示在單獨的控制線程中運行的活動。有兩種方法可以指定這種活動,給構造函數傳遞回調對象,或者在子類中重寫run() 方法。其他方法(除了構造函數)都不應在子類中被重寫。換句話說,在子類中只有__init__()和run()方法被重寫。
一旦線程對象被創建,它的活動需要通過調用線程的start()方法來啟動。這方法再調用控制線程中的run方法。
一旦線程被激活,則這線程被認為是'alive'(活動)。當它的run()方法終止時-正常退出或拋出未處理的異常,則活動狀態停止。isAlive()方法測試線程是否是活動的。
一個線程能調用別的線程的join()方法。這將阻塞調用線程,直到擁有join()方法的線程的調用終止。
線程有名字。名字能傳給構造函數,通過setName()方法設置,用getName()方法獲取。
線程能被標識為'daemon thread'(守護線程).這標志的特點是當剩下的全是守護線程時,則Python程序退出。它的初始值繼承于創建線程。這標志用setDaemon()方法設置,用isDaemon()獲取。
存在'main thread'(主線程),它對應于Python程序的初始控制線程。它不是后臺線程。
有可能存在'dummy thread objects'(啞線程對象)被創建。這些線程對應于'alien threads'(外部線程),它們在Python的線程模型之外被啟動,像直接從C語言代碼中啟動。啞線程對象只有有限的功能,它們總是被認為是活動的,守護線程,不能使用join()方法。它們從不能被刪除,既然它無法監測到外部線程的中止。
-
class Thread(group=None, target=None, name=None, args=(), kwargs={})
-
構造函數能帶有關鍵字參數被調用。這些參數是:
group 應當為 None,為將來實現ThreadGroup類的擴展而保留。
target 是被 run()方法調用的回調對象. 默認應為None, 意味著沒有對象被調用。
name 為線程名字。默認,形式為'Thread-N'的唯一的名字被創建,其中N 是比較小的十進制數。
args是目標調用參數的tuple,默認為()。
kwargs是目標調用的參數的關鍵字dictionary,默認為{}。
如果子線程重寫了構造函數,它應保證調用基類的構造函數(Thread.__init__()),在線程中進行其他工作之前。
-
start()
- 啟動線程活動。
在每個線程對象中最多被調用一次。它安排對象的run() 被調用在一單獨的控制線程中。
-
run()
- 用以表示線程活動的方法。
-
你可能在子類重寫這方法。標準的 run()方法調用作為target傳遞給對象構造函數的回調對象。如果存在參數,一系列關鍵字參數從args和kwargs參數相應地起作用。
-
join([timeout])
- 等待至線程中止。這阻塞調用線程直至線程的join() 方法被調用中止-正常退出或者拋出未處理的異常-或者是可選的超時發生。
-
當timeout參數未被設置或者不是None
,它應當是浮點數指明以秒計的操作超時值。因為join()總是返回None
,你必須調用isAlive()來判別超時是否發生。
-
當timeout 參數沒有被指定或者是None
時,操作將被阻塞直至線程中止。
-
線程能被join()許多次。
-
線程不能調用自身的join(),因為這將會引起死鎖。
-
在線程啟動之前嘗試調用join()會發生錯誤。
-
getName()
- 返回線程名。
-
setName(name)
- 設置線程名。
-
這名字是只用來進行標識目的的字符串。它沒有其他作用。多個線程可以取同一名字。最初的名字通過構造函數設置。
-
isAlive()
- 返回線程是否活動的。
-
大致上,線程從 start()調用開始那點至它的run()方法中止返回時,都被認為是活動的。模塊函數enumerate()返回活動線程的列表。
-
isDaemon()
- 返回線程的守護線程標志。
-
setDaemon(daemonic)
- 設置守護線程標志為布爾值daemonic。它必須在start()調用之前被調用。
-
初始值繼承至創建線程。
-
當沒有活動的非守護線程時,整個Python程序退出。
參見:Python Library Reference
posted @
2008-06-24 16:54 len 閱讀(9165) |
評論 (0) |
編輯 收藏
介紹
cURL是一個利用URL語法的文件傳輸工具,是基于libcurl的前端命令行工具。它支持很多協議:FTP, FTPS, HTTP, HTTPS, GOPHER, TELNET, DICT, FILE 以及 LDAP。 它同樣支持HTTPS認證,HTTP POST方法, HTTP PUT方法, FTP上傳, kerberos認證, HTTP上傳, 代理服務器, cookies, 用戶名/密碼認證, 下載文件斷點續傳, 上載文件斷點續傳, http代理服務器管道( proxy tunneling), 甚至它還支持IPv6, socks5代理服務器,通過http代理服務器上傳文件到FTP服務器等等,功能十分強大。
除了使用curl命令行直接進行相關的網絡操作,你也可以自由地使用libcurl,它是用C語言編寫的,可以綁定到眾多的編程語言中,如C,C++,PHP,Python,Perl,Java等等。你可以很方便地利用libcurl,在程序中進行一些網絡傳輸工作,來代替一些語言的內置,使你的知識可重用。在Unix工作環境下,你可以用curl代替wget和ftp等工具,并能將這種學習經驗遷移到將來使用libcurl來完成一些自動化任務。
curl是瑞典curl組織開發的,可以通過http://curl.haxx.se/來獲取更詳細的信息和下載文件。
curl命令行工具使用
curl太強大了,只能對其HTTP的部分作一簡單的介紹,其他選項可以參見其附帶的手冊。它的后端庫的使用也非常方便,主要也是在選項設置上,跟命令行基本無異。
用法
curl [選項] [URL...]
URL 語法
URL語法是跟協議相關的,具體細節可參見RFC 3986
可以指定多個URLs或者部分URL地址,通過花括號{}進行分割:
http://site.{one,two,three}.com
或者用[]使用字母序:
ftp://ftp.numericals.com/file[1-100].txt
ftp://ftp.numericals.com/file[001-100].txt (有前導零)
ftp://ftp.letters.com/file[a-z]].txt
當前序列嵌套不被支持,但是還是可以使用下列的樣式:
http://any.org/archive[1996-1999]/vol[1-4]/part{a,b,c}.html
可以在命令行指定任意數量的URLs,它們將以指定的順序被取回。
從curl7.15.1開始指定可以范圍步長,所以可以得到第n個數或字母:
http://www.numericals.com/file[1-100:10].txt
http://www.letters.com/file[a-z:2].txt
如果使用了protocal://前綴,curl會將嘗試你想使用的協議。它默認使用HTTP,但是其他一些協議也常被用作主機名。比如說,以"ftp"打頭的主機名,curl會假定你想使用ftp協議。
Curl會嘗試對多文件傳輸使用重連接,可以使從同一服務器獲取很多文件時,不會進行多次的連接。這種做法改進了速度,當然這只會在同一命令行中指定的文件啟用,而不會在獨立的Curl調用時使用。
進度指示器
curl通常在操作時會顯示一個進度指示器,來指明當前的傳輸量,傳輸速度和預計的剩余時間等等。
但是,即然curl默認將數據顯示在終端,如果你調用curl進行操作,它會將數據打印到終端上,這時它會禁用掉進度指示器,否則這些會將輸出信息搞亂掉。
如果在進行HTTP的POST或PUT請求時,你想將輸出重定向到文件中,可以使用shell的重定向符(>),或者類似的-o[file]選項。
但是在FTP上傳并不會這樣,這些操作不會將數據插入到終端中。
如果想使用進度欄,而不是常規的指示器,那么-#會非常有幫助。
常用的HTTP選項
-A/--user-agent<agent string>
(HTTP)指定用戶代理字符串發送給HTTP服務器。如果這個字段沒有被設為"Mozilla/4.0",某些CGIs將不會正常工作。如果在字符串中存在空白字符,需要用單引號標識。這個字段值也可用-H/--header選項進行設置。
如果這選項被多次設置,最后的設置將起作用。
-b/--cookie<name=data>
(HTTP)將data作為cookie傳給HTTP服務器,這數據當然是在使用了"Set-Cookie:"后,先前從服務端接收到的。這數據應是"NAME1=VALUE1;NAME2=VALUE2"的格式。
如果沒有"="字符,它將會當作先前存儲cookie行的文件名,如果它能被匹配的話。使用這選項,也能激活"cookie parser",它使curl記錄傳入的cookies數據。將它與-L/--locaion選項組合將會更加便利。被讀取cookie的文件格式應當是文本HTTP頭或者Netscape/Mozilla cookie文件格式。
注意:被-b/--cookie指定的文件只能作為輸入使用。沒有cookie會存儲在這文件中。為了存儲cookie,應使用-c/--cookie-jar選項或者直接將HTTP頭輸出到文件中,用-D/--dump-header選項。這選項可以設置多次,但是只有最后一個起作用。
-connect-timeout<seconds>
以秒計的最大超時,用于進行服務器連接時。這只在連接階段起作用,一旦curl連接建立,這選項將不再起作用。
-c/--cookie-jar<file name>
指定在完成一系列操作后,需要將全部的cookie信息保存到哪個文件中。Curl會將先前讀取的cookie的信息和從服務器返回的信息一起保存。如果沒有cookie信息,則不會進行寫文件。cookie信息的文件將與Netscape cookie文件格式保存。如果文件名被設置為'-',則將cookie打印至終端。
注意:如果cookie-jar不能被創建寫入,整個curl操作也不會失改,甚至不會向你報告錯誤.使用-v將會得到警告顯示,但也只能在可能導致發生致命錯誤的情況才會顯示。
--create-dirs
這與-o選項配合使用,curl會在需要時建立本地文件夾結構。這選項會創建在-o選項中涉及到的文件夾。如果-o選項中的文件名沒有使用到文件夾,或者所需的文件夾已經存在,則不會有文件夾創建。
-D/--dump-header<file>
將協議頭寫到指定的文件中。當你想存儲HTTP站點發給你的數據時,這選項非常有用。在協議頭中的cookie將來可以用curl的另外調用來進行讀取,那就是-b/--cookie選項。但是-c/--cookie-jar選項將會是更好的存儲cookie信息的方法。
當使用FTP協議時,ftp服務器的應答信息將相應地當作成協議頭,然后被存儲。
-p/-proxytunnel
當HTTP代理被設置后(-x/--proxy),選項會使不是HTTP協議的傳輸試圖通過代理隧道,而不是表現得HTTP類似的操作形為。代理隧道的方法是通過HTTP服務器直接使用CONNECT請求,讓代理直接連接到curl隧道所請求的遠程端口號的方式來實現的。
-o/--output<file>
將輸出信息打印到文件中,而不是終端。可使用{}或者[]取回多個文檔,可在file指定格式中的'#'跟一數字,這樣這變量將會由取回的URL字符串所取代。如下:
curl http://{one,two}.site.com -o "file_#1.txt"
如果有多個變量,可以寫成下面的樣子:
curl http://{site.host}.host[1-5].com -o "#1_#2"
你可對任意數量的URL使用同樣多的這個選項
-x/--proxy<proxyhost[:port]>
使用指定的HTTP代理,如果端口號沒有被指定,默認為1080.
這選項會覆蓋環境變量中代理服務器的設置。如果環境變量中設置了代理,可將proxy設置為空字符串,來覆蓋環境變量中的設置。
注意:所有通過HTTP代理的操作都會自動轉化為HTTP協議。這意味著一些特定協議的操作將會變得無效。這不會有問題,如果在設置了-p/--proxytunnel選項來通過代理隧道進行操作。
簡單示例
獲取cppblog首頁,打印至終端
>curl http://www.shnenglu.com
重定向,保存到文件cppblog.html
>curl http://www.shnenglu.com
作用同上,使用選項
>curl -o baidu.html http://www.baidu.com
使用http代理,可指定IP和端口
>curl -x 202.127.98.43:80 -o baidu.html http:www.baidu.com
在訪問一些論壇時,常常要求啟用cookie,因為這些網站需要啟用cookie來記錄sessioin信息,這時需要選項-D,將cookie信息保存起來
>curl -o cpp.html -c len@cppblog.com[1].txt http://www.shnenglu.com
先前保存的cookie信息返回給網站,這通常會傳回你的一些用戶信息。
>curl -o cpp.html -c len@cppblog.com[2].txt -b len@cppblog.com[1].txt http://www.shnenglu.com
posted @
2008-06-21 16:33 len 閱讀(7176) |
評論 (1) |
編輯 收藏
介紹
命令行接口是普遍,基礎的人機交互接口,從命令行提取程序的運行時選項的方法有很多。你可以自己編寫相對應的完整的解析函數,或許你有豐富的C語言編程經驗,熟知getopt()函數的用法,又或許使用Python的你已經在使用optparse庫來簡化這一工作。大家在平時不斷地談及到“不要重復造輪子”,那就需要掌握一些順手的庫,這里介紹一種C++方式來解析命令行選項的方法,就是使用Boost.Program_options庫。
program_options提供程序員一種方便的命令行和配置文件進行程序選項設置的方法。使用program_options庫而不是你自己動手寫相關的解析代碼,因為它更簡單,聲明程序選項的語法簡潔,并且庫自身也非常小。將選項值轉換為適合的類型值的工作也都能自動完成。庫有著完備的錯誤檢查機制,如果自己手寫解析代碼時,就可能會錯過對一些出錯情況的檢查了。最后,選項值不僅能從命令行獲取,也能從配置文件,甚至于環境變量中提取,而這些選擇不會增加明顯的工作量。
示例說明
以下面簡單的hello程序進行說明,默認打印hello world,如果傳入-p選項,就會打印出人的姓名,另外通過傳入-h選項,可以打印出幫助選項。略微看一眼代碼文件和相應的屏幕輸入輸出,然后我們再一起來看看這些是如何發生的。
//hello.cpp
#include <iostream>
#include <string>
#include <boost/program_options.hpp>
using namespace std;
int main(int argc, char* argv[])
{
using namespace boost::program_options;
//聲明需要的選項
options_description desc("Allowed options");
desc.add_options()
("help,h", "produce help message")
("person,p", value<string>()->default_value("world"), "who")
;
variables_map vm;
store(parse_command_line(argc, argv, desc), vm);
notify(vm);
if (vm.count("help")) {
cout << desc;
return 0;
}
cout << "Hello " << vm["person"].as<string>() << endl;
return 0;
}
下面是在Windows命令提示符窗口上的輸入輸出結果,其中">"表示提示符。
>hello
Hello world
>hello -h
Allowed options:
-h [ --help ] produce help message
-p [ --person ] arg (=world) who
>hello --person len
Hello len
首先通過options_description類聲明了需要的選項,add_options返回了定義了operator()的特殊的代理對象。這個調用看起來有點奇怪,其參數依次為選項名,選項值,以及選項的描述。注意到示例中的選項名為"help,h",是因為聲明了具有短選項名和長選項名的選項,這跟gnu程序的命令行具有一致性。當然你可以省略短選項名,但是這樣就不能用命令選項簡寫了。第二個選項的聲明,定義了選項值為string類型,其默認值為world.
接下來,聲明了variables_map類的對象,它主要用來存儲選項值,并且能儲存任意類型的值。然后,store,parse_command_line和notify函數使vm能存儲在命令行中發現的選項。
最后我們就自由地使用這些選項了,variables_map類的使用就像使用std::map一樣,除了它必須用as方法去獲取值。如果as方法調用的指定類型與實際存儲的類型不同,就會有異常拋出。
具有編程的你可能有這樣的經驗,使用cl或gcc對源文件進行編譯時,可直接將源文件名放置在命令行中,而無需什么選項字母,如gcc a.c之類的。prgram_options也能處理這種情況,在庫中被稱為"positional options"(位置選項),但這需要程序員的一點兒幫助才能完成。看下面的經過對應修改的代碼,我們無需傳入"-p"選項,就能可指定"person"選項值
positional_options_description p;
p.add("person", -1);
store(command_line_parser(argc, argv).options(desc).positional(p).run(), vm);
>hello len
Hello len
前面新增的兩行是為了說明所有的位置選項都應被解釋成"person"選項,這里還采用了command_line_parser類來解析命令行,而不是用parse_command_line函數。后者只是對前者類的簡單封裝,但是現在我們需要傳入一些額外的信息,所以要使用類本身。
選項復合來源
一般來說,在命令行上指定所有選項,對用戶來說是非常煩人的。如果有些選項要應用于每次運行,那該怎么辦呢。我們當然希望能創建出帶有些常用設置的選項文件,跟命令行一起應用于程序中。當然這一切需要將命令行與配置文件中的值結合起來。比如,在命令行中指定的某些選項值應該能覆蓋配置文件中的對應值,或者將這些值組合起來。
下面的代碼段將選項通過文件讀取,這文件是文本格式,可用"#"表示注釋,格式如命令行中的參數一樣,選項=值
ifstream ifs("config.cfg");
store(parse_config_file(ifs,config),vm);
notify(vm);
參考
Boost.prgram_options庫文檔
posted @
2008-06-15 21:03 len 閱讀(13625) |
評論 (0) |
編輯 收藏
涉及到軟件:Xmanager 1.3.9 / Windows xp, ubuntu hardy
第一步,在ubuntu機器上配置好gdm,修改/etc/gdm/gdm.conf-custom,對照添加如下內容:
[security]
DisallowTCP=false
[xdmcp]
Enable=true
第二步,性能調優。這步非常關鍵,不然使用Xmanager登陸速度非常慢,且會報錯,主要原因是gnome使用Esound進行聲音數據的傳送,需要使用TCP 16001端口。所以我建設在ubuntu關掉混音選項。
系統-首選項-音效-音效,將“允許軟件混音”不要勾選上,
系統-首選項-字體-字體渲染,選擇"單色",在“細節”的“字體渲染細節”中的平滑和微調選項,都選擇"無"。
有用的參考:http://www.netsarang.com/products/xmg_faq.html
posted @
2008-06-01 21:10 len 閱讀(7690) |
評論 (0) |
編輯 收藏
Vim是功能強大的文本編輯器,但是每個工具都有其針對的適用群體。如果你只是偶爾做些文本編輯工作的話,那靈活而又顯得繁瑣的設置,以及特別的操作方式可能不適合你。但是你是跟我一樣,是個平平凡凡的程序員,每天要花費大量時間在寫代碼,把弄著各式各樣的程序語言:C\C++,Python,Tcl,Html,Xml,...,那么你可能需要像Vim這樣的工具,即使你要在它上面花費些時間去熟悉和適應它。
先講述一下,我跟Vim相處的過程,這是個從認識,到拋棄,到再認識,到再學習,到喜歡的過程。最早接觸到Vim是在Solaris上,需要修改編輯一些配置文件,看著其他工程師們手指隨意地在鍵盤上敲擊,就完成內容的修改,根本沒有動用到什么鼠標,那是好生羨慕。嚴格意義來說,那時候碰到還不是Vim,只是VI而已。在終端上工作,沒有什么Notepad之類的程序,只好把指令抄在紙上,查查網上的資料,學會了h,j,k,l,w,q,e,這幾個簡單指類來進行簡單的文本查看工作,僅此而己。后來在Windows上安裝了VIM,但是掙騰了幾下,沒有適應過來,也就只好使用UltraEdit了。UltraEdit對一般的純文本,按Windows習慣來說是蠻好使的。再后來,玩了會兒ruby,又裝起了Vim,但是那時候的對Vim的使用也只是限于上面的簡單的指令,再加上Vim的插件,來完成語法高亮,ruby中的MVC文件的方便跳轉而已,還是沒有習慣Vim,有時候還是不經意用UltraEdit來打開查看編輯文件。直到最近,需要編寫Docbook,以及用Python,才真正花費了大量時間來學習使用Vim,才真正認識到到它的可愛。
接著說說,我為什么使用Vim,覺得值得學習它,喜歡它的理由吧,純粹以自己的觀點來敘述。
?跨平臺性,無論在Windows,Linux,Solaris,FreeBSD等等操作系統上,以及一些名都沒有聽過的系統上,你都可以找到它。這樣就保證了你的學習投資的保值性,就拿UltraEdit做對比吧,即使你在UltraEdit上學會靈活運用許多功能,到了Linux上,你在這部分學習投資就沒有價值了,你可能需要找其他稱手的編輯器,然后再進行學習一些功能。特別在一些古老的大型機上的系統上,即使沒有Vim,一般來說,還有Vi的,這樣一般簡單的操作命令還是可復用的。如果你確定你一直只呆在Windows上可忽略這一點。
開源免費,Vim是開源軟件,意味著你可以自由使用,修改,查看它的代碼。我對FreeSoftware,Open Source,Copyright,這些都是持中間立場的。對于自由查看,修改程序代的保證,有總比沒有好。對于盜版軟件,你有能力還是不要使用的好。正是這一特性,也是促使我放棄UE,投向Vim的重要原因。如果你對于使用盜版軟件蠻不在乎,或你有財力購買正版軟件,也可忽視這一條。
支持多種編程語言,Vim是程序員的編輯器,當然對程序員是非常友好的。它對C,C++,Python,Perl,Tcl,Ruby,PHP等等,以及一大堆我沒有聽過見過的語言,以語法著色,代碼縮進等基本支持,還有一些其他特性。比如,我在編輯XML時,它能提供自動封閉標記的支持。因此如果你有對多種格式的文本編輯需要,那么你就有了一個編輯的大平臺,不需用再裝一大堆針對某個格式特定的編輯器了。正如跨平臺性一樣,你只要一次投資,多次回報。如果你專注于某一格式文件的工作,那這一點同樣對于你來說是沒有用的。
高效地編輯,Vim的操作方式相對于Windows上呆久了的人來說,是蠻奇特的,這一點我深有體會。但是正如很多人講的那樣,你掌握了其操作后,發現它會大大增進你的編輯速度。你的雙手根本不用離開鍵盤,就完成了許多事情,可以讓鼠標歇會兒了。如果你特別鐘愛鼠標,或只偶爾打打字,那么我說的這點,同樣對你沒有用。
靈活的設置,Vvim可自定義的地方太多了,你可以自定義鍵盤映射,語法著色,縮進,格式等等。所以你在網上可以看到許多人貼著自己的vimrc配置文件,配置著自己喜歡的作業環境。如果你需要開盒即用的工具,那么這點對你的吸引力就不大了。
安裝
可到VIM官網,選擇Self-install executable形式的安裝包下載安裝。
幫助
幫助非常重要,VIM帶有我認為非常好的幫助系統,可以獲取你需要的任何有關VIM的詳細信息。使用幫助非常簡單,只需要:help安裝即可。安裝后程序帶的是英文幫助,如果你對英文不是特別適應的話,可以去http://vimcdoc.sourceforge.net/下載安裝中文幫助,或像我一樣直接使用在線的中文幫助
操作方法
對于基本操作方法,通過Vim的教程,你應該能很好的掌握了。一些常見的設置,關于特定類型的配置,因人而異,不想多述。我會列出一些認為比較好的參考文章,置于文尾供參考。但在下面,我還是在Windows下的Vim的使用做點說明,或許你現在用不上。
Vim文件夾結構
安裝完Vim后,你在其安裝目錄下應有vim$ver($ver是版本號)和vimfiles兩個文件夾。其中vim$ver是vim的程序運行時目錄,在里面會看到gvim.exe(vim的GUI),vim.exe,xxd.exe等程序,一大堆的dll動態鏈接庫,還有就是color(語法著色),doc(幫助說明),indent(縮進)等文件夾。在vimfiles內,也會看到color,doc,indent等類似的文件夾,但它們里面沒有文件。vim$ver和vimfiles兩者有什么區別呢,vim$ver是運行時文件目錄,vimfiles相當于個人配置目錄,常常有文章說在linux下將什么插件放進.vim下的plugin等等之類的,其.vim在windows下就相當于vimfiles。
標簽頁
Tabpage是Vim后增的功能,類似于UltraEdit的標簽頁。也想在Windows下使用Untraledit一樣,在同一個VIM實例中打開多個文件的話,需要做些小修改。在注冊表中刪除"HKCR\*\shellex\ContextMenuHandlers\gvim\"主鍵,然后在Shell下新項"Vim 編輯",再在其下新建command項,然后修改其值為$vimruntime\gvim.exe -p --remote-tab-silent "%1",其中$vimruntime修改為你系統中VIM實際運行目錄。如果你不知道$vimruntime的值,可以打開gvim,輸入:echo $vimruntime。你想雙擊關聯文件,也在同一實例打開的話,查找注冊表中gvim相關項,將$vimruntime\gvim.exe改為上述的值即可,主要是HKLM\software\classes\application\gvim.exe\shell\edit\command下的值。
文件編碼
具體可參見"Vim實用技術:實用技巧"。我推薦內部編碼使用utf-8,以支持國際化,即encoding=utf-8。這需要在_vimrc中進行設置,網上常有人啟用這一選項后Vim菜單和消息出現亂碼。據我的經驗,需要將這encoding=utf-8寫在_vimrc最開頭,然后設置language message,可參見我的_vimrc文件。
vimrc文件
Vim使用中,配置文件vimrc是非常重要的,用:echo $myvimrc,來查看你的vimrc在哪里。
如果這為空的話,你可以在$vim目錄,建一新的_vimrc文件。
我的vimrc文件
set encoding=utf-8
set termencoding=gbk
set nocompatible " We're running Vim
set nobackup "We don't need the backup file
set showmatch "Show where the bracket match
set showcmd
set ruler "Show the line and column number
set hlsearch "Highlight the search key
set backspace=indent,eol,start
set fileencodings=ucs-bom,utf-8,chinese
set guifont=courier_new:h10
set autoindent
syntax on " Enable syntax highlighting
filetype plugin indent on " Enable filetype-specific indenting and plugins
language message zh_CN.utf-8 " Use chinese message
color zellner " Color theme
其中termencoding=gbk是因為windows中的“命令提示符”窗口只能使用gbk編碼,不能像Gnome中的Console那樣用utf-8。不設置的情況下,使用“命令提示符”下的vim,而不是gvim時,會出亂碼。在設置文件中的色彩和字體,可以先在gvim菜單中設置,然后將你所喜好的,添加到_vimrc文件中。看到我的vimrc文件,你是不是感覺特別短。因為我把許多跟文件類型的相關設置放在其對應的腳本里,扔在vimfiles文件夾了。在vimrc里,例如常見的空格,制表符,縮進都沒有在這配置。
雜項
Vim中一些內置的變量,你都可以通過:echo varname來查看值,比如::echo $myvimrc
這些變量,注意大小寫,常用的有
$VIM Vim的安裝目錄
$vimruntime Vim運行時目錄
$myvimrc 用戶的_vimrc文件
$home 用戶的主目錄
我常常使用:e $myvimrc來編輯我的vimrc文件,非常方便。
對一些帶值的配置選項,你可以用:set optionname來查看其當前值,或用:set optionname=val來更改其值.比如:set fileformat查看文件格式,因為dos,unix,mac對于換行是不一樣的。:set filemat=unix的話,換行將用LF,而不是dox\windows下的CR,LF。
參考鏈接
IBM開發中心非常實在的Vim實用技術系列:
Vim實用技術(1)-實用技巧
Vim實用技術(2)-常用插件
Vim實用技術(3)-定制Vim
Easwy的博客,里面有用的信息,更多的Vim資源鏈接
Vim專欄
posted @
2008-05-25 20:19 len 閱讀(5722) |
評論 (13) |
編輯 收藏
Boost.Lambda是什么?
Boost Lambda庫是C++模板庫,以C++語言實現了lambda抽象.Lambda這個術語來自函數編程語言和lambda閉包理論,lambda抽象實際上定義了匿名函數.了解過C#新引入的匿數函數特性或Lisp編程的人,對這些概念理解會有很大幫助.Lambda庫設計的主要動機是為STL算法提供靈活方便的定義匿名函數對象的機制.這個Lambda庫究竟是有什么用呢?代碼勝千言!看下面將STL容器中的元素打印到標準輸出上的代碼.
for_each(a.begin(), a.end(), std::cout << _1 << ' ');
表達式std::cout << _1 << ' '定義了一元函數對象.變量_1是函數的形參,是實參的占位符.每次for_each的迭代中,函數帶著實際的參數被調用,實際參數取代了占位符,然后函數體里的內容被執行.Lambda庫的核心就是讓你能像上面所展示的那樣,在STL算法的調用點,定義小的匿名函數對象.
Lambda庫的安裝
Lambda庫只由頭文件組成,這就意味著你不需要進行任何編譯,連接,生成二進制庫的動作,只需要boost庫頭文件路徑包含進你的工程中即可使用.
與現代的C++語言一樣,在使用時你需要聲明用到的名字空間,把下列的代碼包含在你的源文件頭:
using namespace boost::lambda;
Boost Lambda庫的動機
在標準模板庫STL成為標準C++的一部分后,典型的STL算法對容器中元素的操作大都是通過函數對象(function objects)完成的.這些函數作為實參傳入STL算法.
任何C++中以函數調用語法被調用的對象都是函數對象.STL對某些常見情況預置了些函數對象.比如:plus,less,not1下面就是標準plus模板的一種可能實現: template <class T>
struct plus : public binary_function <T, T, T> {
T operator()(const T& i, const T& j) const {
return i + j;
}
};
基類binary_function<T, T, T>包含了參數和函數對象返回類型的類型定義,這樣可使得函數對象可配接.
除了上面提到的基本的函數對象外,STL還包含了binder模板,將可配接的二元函數中的某個實參固定為常量值,來創建一個一元函數對象.比如:
class plus_1 {
int _i;
public:
plus_1(const int& i) : _i(i) {}
int operator()(const int& j) { return _i + j; }
};
上面的代碼顯性地創建了一個函數對象,將其參數加1.這樣的功能可用plus模板與binder模板(bind1st來等效地實現.舉例來說,下面的兩行表達式創建了一個函數對象,當它被調用時,將返回1與調用參數的和.
plus_1(1)
bind1st(plus<int>(), 1)
plus<int>就是計算兩個數之和的函數對象.bind1st使被調用的函數對象的第一個參數綁定到常量1.作為上面函數對象的使用示例,下面的代碼就是將容器a中的元素加1后,輸出到標準輸出設備:
transform(a.begin(), a.end(), ostream_iterator<int>(cout),
bind1st(plus<int>(), 1));
為了使binder更加通用,STL包含了適配器(adaptors)用于函數引用與指針,以及成員函數的配接.
所有這些工具都有一個目標,就是為了能在STL算法的調用點有可能指定一個匿名的函數,換句說,就是能夠使部分代碼片斷作為參數傳給調用算法函數.但是,標準庫在這方面只做了部分工作.上面的例子說明用標準庫工具進行匿名函數的定義還是很麻煩的.復雜的函數調用表達式,適配器,函數組合符都使理解變得困難.另外,在運用標準庫這些方法時還有明顯的限束.比如,標準C++98中的binder只允許二元函數的一個參數被綁定,而沒有對3參數,4參數的綁定.這種情況在TR1實施后,引進了通用的binder后可能改善,對于使用MSVC的程序員,有興趣還可以查看下微軟針對VS2008發布的TR1增強包.
但是不管怎樣,Lambda庫提供了針對這些問題比較優雅的解決方法:
對匿名函數以直觀的語義進行創建,上面的例子可改寫成:
transform(a.begin(), a.end(), ostream_iterator<int>(cout),
1 + _1);
更直觀點:
for_each(a.begin(), a.end(), cout << (1 + _1));
絕大部分對函數參數綁定的限制被去除,在實際C++代碼中可以綁定任意的參數
分離的函數組合操作不再需要了,函數組合被隱性地支持.
Lambda表達式介紹
Lambda表達在函數式編程語言中很常見.在不同語言中,它們的語法有著很大不同,但是lambda表達式的基本形式是:
lambda x1...xn.e
lambda表達式定義了匿名函數,并由下列的元素組成
- 函數的參數:x1...xn
- 表達式e,以參數x1...xn的形式計算函數的值
一個簡單的lambda表達式的例子是:
(lambda x y.x+y) 2 3 = 2 + 3 = 5
在lambda表達式的C++版本中,表達式中x1...xn不需要,已預定義形式化的參數.在現在Boost.Lambda庫中,存在三個這樣的預定義的參數,叫做占位符:_1,_2,和_3.它們分別指代在lambda表達式中的第一,二,三個參數.比如,下面這樣的lambda表達式:
lambda x y.x+y
C++定義的形式將會是這樣:
_1 + _2
因此在C++中的lambda表達式沒有語義上所謂的關鍵字.占位符作為運算符使用時就隱性地意味著運算符調用是個lambda表達式.但是只有在作為運算符調用才是這樣.當Lambda表達式包含函數調用,控制結構,轉換時就需要特殊的語法調用了.更為重要的是,作為函數調用是需封裝成binder函數的形式.比如,下面這個lambda表達式:
lambda x y.foo(x,y)
不應寫成foo(_1,_2),對應的C++結構應如下:
bind(foo, _1, _2)
對于這種表達式,更傾向于作為綁定表達式bind expressions
lambda表達式定義了C++的函數對象,因此,對于函數調用的形式跟其他的函數對象一樣,比如:(_1 + _2)(i, j).
性能
性能,運行效率,總是C++程序員關心的話題.理論上,相對于手寫循環代碼,使用STL算法和Lambda函數對象的所有運行開銷,可以通過編譯優化消除掉.這種優化取決于編譯器,實際中的編譯器大都能做到.測試表明,性能會有下降,但是影響不大,對于代碼的效率和簡潔之間的權衡,只能由程序員自己做出判斷了.
Lambda庫的設計與實現中大量運用了模板技術,造成對于同一模板需要大量的遞歸實例化.這一因素可能使構建復雜邏輯的lambda表達式,不是一個非常理想的做法.因為編譯這些表達式需要大量的內存,從而使編譯時間變得非常慢,這在一些大型項目中會更加突出.還有在發生編誤錯誤時,引發的大量錯誤信息,不能有效地指出真正錯誤之處.最后點,C++標準建議模板的嵌套層次不要超過17層來防止導致無限遞歸,而復雜的Lambda表達式模板會很容易超過這一限制.雖然大多數編譯器允許更深層次的模板嵌套,但是通常需要顯性地傳入一個命令行參數才能做到.
參考
大多數內容是從Boost.Lambday庫在線文檔參考翻譯而成
posted @
2008-05-18 16:03 len 閱讀(8617) |
評論 (5) |
編輯 收藏
"Designing Qt-Style C++ APIs" by Matthias Ettrich
http://doc.trolltech.com/qq/qq13-apis.html
翻譯這篇文章的目的不是讓人了解Qt,而是讓人試著學習點C++編程的軟技能。我從原文中得到的一些風格上的體會,也希望你能從中有所收獲.(譯者注)
我們在Trolltech做了大量研究來改進Qt開發體驗.在這篇文章中,我將分享我們的一些成果,呈現我們在進行Qt 4設計時所使遵循的原現,并向你展示如何將它們應用到你的代碼中.
設計應用程序接口(APIs)是有難度的.它是像跟設計編程語言一樣困難的藝術.要遵循許多不同的的原則,這些原則中的許多還彼此沖突.
現今的計算機教育過多關注于算法和數據結構,很少去關注隱藏在程序設計語言和程序框架后面的那些設計原則.這使得程序員們面對日益重要的任務,創建可復用的組件,毫無準備.
在面向對象語言出現前,通用的可復用的代碼大都由庫提供者而不是應用程序開發者來編寫.在Qt世界中,這種情況已發生了很大的變化.在用Qt編程其實就是在寫新的組件.典型的Qt應用程序都存在某些自定義的組件,在整個應用程序中被復用.相同的組件常常作為其他程序的一部分被開發出來.KDE,K桌面環境,甚至使用許多附加庫,來進一步擴展Qt,實現許多額外的類.
但是一個優秀,高效的C++ API究竟是怎樣子呢?它的好壞取決于許多因素,比如說,手頭上的任務和特定目標群體.優秀的API具有很多特性,它們的一些是普遍所要期望的,另一些是針對特定問題域的.
優秀API的六個特性
API對于程序員就相當于GUI對于最終用戶.API中'P'代表程序員(Programmer),而不是程序(Program),強調這一點是為了說明API是讓程序員使用的,程序員是人而不機器.
我們認為APIs應當精簡而完備,具有清晰簡單的語義,直觀,易記且應使代碼具有可讀性.
-
精簡性:精簡的API具有盡可能少的類和公共成員.這使得理解,記憶,調試,更改API更加容易.
-
完備性:完備的API意味著擁有應具有的期望功能.這可能使與API保持精簡性相沖突.還有,如果成員函數放在不相匹配的類中,那么許多使用這個功能函數的潛在用戶會找不到它.
-
清晰簡單的語義:正如與其他設計工作一樣,你應該準守最小驚議原則.讓通常的任務簡單,罕見的任務應盡可能簡單,但它不應成為重點.解決特定的問題.不要使解決方法具有普適作用,當它們不需要的時候.
-
直觀性:與計算機有關的其他事情一樣,API應具有直觀性.不同經歷和背景會導致對哪些是直觀,哪些不是直觀的不同看法.如果對非專業的用戶在不需要閱讀文檔下能立即使用API,或對這個API不了解的程序員能理解使用了API的代碼,那么這API就是具有直觀性.
-
易記:為了使API容易記憶,使用一致且精準的命名規范.使用容易識別的模式和概念,避免使用縮寫.
-
能生成可讀生代碼:代碼只寫一遍,卻要閱讀許多遍(調試或更改).可讀性的代碼有時候可能需要多敲些字,但是從產品生命周期中可節省很多時間.
最后,請記住:不同的用戶使用API的不同部分.當簡單地使用Qt類的實例可能有直觀性,但這有可能使用戶在閱讀完有關文檔后,才能嘗試使用其中部分功能.
方便性陷阱
通常的誤讀是越少的代碼越能使你達到編寫更好的API這一目的.請記住,代碼只寫一遍,卻要一遍又一遍地去理解閱讀它.比如:
QSlider *slider = new QSlider(12, 18, 3, 13, Qt::Vertical,
0, "volume");
可以會比下面的代碼更難閱讀(甚至于編寫)
QSlider *slider = new QSlider(Qt::Vertical);
slider->setRange(12, 18);
slider->setPageStep(3);
slider->setValue(13);
slider->setObjectName("volume");
布爾參數陷阱
布爾參數常常導致難以閱讀的代碼.特別地,增加某個bool參數到現存的函數一般都會是個錯誤的決定.在Qt中,傳統的例子是repaint(),它帶有一個可選的布爾參數,來指定背景是否刪除(默認是刪除).這就導致了代碼會像這樣子:
widget->repaint(false);
初學者可能會按字面義理解為,"不要重繪!"
自然的想法是bool參數節省了一個函數,因此減少了代碼的臃腫.事實上,這增加了代碼的臃腫,有多少Qt用戶真正知道下面這三行代碼在做什么呢?
widget->repaint();
widget->repaint(true);
widget->repaint(false);
好一點的API代碼可能看起來像這樣:
widget->repaint();
widget->repaintWithoutErasing();
在Qt 4中,我們解決這個問題的辦法是,簡單地去除掉不刪除widget而進行重繪的可能性.Qt 4對雙重緩沖的原生支持,會使這功能被廢棄掉.
這里有些例子:
widget->setSizePolicy(QSizePolicy::Fixed,
QSizePolicy::Expanding, true);
textEdit->insert("Where's Waldo?", true, true, false);
QRegExp rx("moc_*.c??", false, true);
顯然的解決辦法就是將bool 參數用枚舉類型來替換.這就是我們在Qt 4中Qstring中的大小寫敏感所做的,比較下面兩個例子:
str.replace("%USER%", user, false); // Qt 3
str.replace("%USER%", user, Qt::CaseInsensitive); // Qt 4
靜態多態
相似的類應該有相似的API.在某種程度上,這能用繼承來實現,也就是運用運行時多態機制.但是多態也能發生在設計時.比如,你將QListBox與QComboBox交換,QSlider與QSpinBox交換,你會發現API的相似性會使這種替換變得比較容易.這就是我們所謂的"靜態多態".
靜態多態也能使記憶API和編程模式更加容易.因而,對一組相關類的相似API有時候比為每個類設計獨特完美的API會更好.
命名藝術
命名有時候是設計API中最重要的事情了.某個類應叫什么名字,某個成員函數又應叫什么名字,都需要好好思考.?
通常的命名規則
有少許規則對所有類型的命名都適應.首先,正如我早先所提到的,不要用縮寫.甚至對用"prev"代表"previous"這樣明顯的縮寫也不會在長期中受益,因為用戶必須記住哪些名字是縮寫.
如果連API自身都不能保持統一,事情自然會變得更壞.比如,Qt 3中有activatePreviousWindow()函數,也有fetchPrev()函數.堅持"沒有縮寫"這條規則,會使創建一致的API更加簡單.
在設計類中,另一重要但是不明顯的規則是盡量保持子類中名字的簡潔易懂.在Qt 3中,這個原則并不總是被遵守.為了說明這一點,我們舉下QToolButton的例子.如果你在Qt 3中對QToolButton調用call name(), caption(), text(), 或 textLabel()成員函數時,你希望會發生什么?那就在Qt設計器中試試QToolButton吧.
-
name 屬性繼承自QObject,用來在調試和測試中指代對象的內部名稱.
-
caption 屬性繼承自QWidget,指代窗體的標題.對于QToolButton沒有什么意思,既然它們都是由父窗體創建的.
-
text 屬性繼承自QButton,通常用于按鈕中,除非useTextLabel為真.
-
textLabel 屬性 在QToolButton中聲明,如果useTextLabel為真,則顯示在按鈕上.
為了可讀性的關系,在Qt4中name 被稱為objectName ,caption被稱為windowTitle,在QToolButton中為了使text明晰,不再有textLabel屬性.
命名類
不應為每個不同的類尋求完美的名字,而是將類進行分給.比如,在Qt 4中所有跟模型有關的視類的部件都用View后綴(QlistView,QTableView,QTreeView),相應的基于部件的類用Widget后綴代替(QListWidget,QTableWidget,QTreeWidge).
枚舉類型和值類型命名
當設計枚舉時,我們應當記住C++中(不像Java或C#),枚舉值在使用時不帶類型名.下面的例子說明了對枚舉值取太一般化的名字的危害:
namespace Qt
{
enum Corner { TopLeft, BottomRight, ... };
enum CaseSensitivity { Insensitive, Sensitive };
...
};
tabWidget->setCornerWidget(widget, Qt::TopLeft);
str.indexOf("$(QTDIR)", Qt::Insensitive);
在上面這行中,Insensitive這個名字什么意思呢?為枚舉類型命名具有指導的原則是最好在每個枚舉值中重復枚舉類型的名字.
namespace Qt
{
enum Corner { TopLeftCorner, BottomRightCorner, ... };
enum CaseSensitivity { CaseInsensitive,
CaseSensitive };
...
};
tabWidget->setCornerWidget(widget, Qt::TopLeftCorner);
str.indexOf("$(QTDIR)", Qt::CaseInsensitive);
但枚舉值之間是一種"或"關系和被用作標志位時,傳統的解決方法是將"或"結果存為int,這樣做是類型不安全的.Qt 4提供了一模板類QFlags<T>,其中T是枚舉類型.Qt為標志類型名稱提供了便利,你能用Qt::Alignment 來代替QFlags<Qt::AlignmentFlag>.
為了方便,我們給枚舉類型單數形式的名稱(只有當只含一個標志位時),給"flags"類型復數形式的名稱,比如:
enum RectangleEdge { LeftEdge, RightEdge, ... };
typedef QFlags<RectangleEdge> RectangleEdges;
在某些情況下,"flags"類型有單數形式的名稱.在這種情況下,枚舉類型以Flag后綴標識:
enum AlignmentFlag { AlignLeft, AlignTop, ... };
typedef QFlags<AlignmentFlag> Alignment;
函數和參數的命名
函數命名中的一條規則就是應能從它的名字清楚地看出函數是否著副作用.在Qt 3中,常函數QString::simplifyWhiteSpace()就違反了這規則.即然它返回QString,而不是像它的名字所表述的那樣修改字符串. 在Qt 4中,這個函數被重命名為QString::simplified().
參數名對于程序員來說是重要的信息來源,即使它們不出現在調用API的代碼中.既然現代的IDE會在程序員編碼時顯示這些參數,所以非常值得在頭文件中給這些參數取恰當的名字,在文檔中同樣使用相同的名字
給布爾型的getter,setter,屬性的命名
給布爾型的getter,setter,屬性取個恰當的名字總是特別困難.getter應該叫checked() 或者還是叫isChecked(),取scrollBarsEnabled()還是areScrollBarEnabled()
在Qt 4中,我們對于getter的函數使用下面的指導原則
- 形容詞就使用is-前綴.比如:
- isChecked()
- isDown()
- isEmpty()
- isMovingEnabled()
但是形容詞應用到復數形式的名詞沒有前綴:
- scrollBarsEnabled(), not areScrollBarsEnabled()
- 動詞沒有前綴,也不使用第三人稱的(-s):
- acceptDrops(), not acceptsDrops()
- allColumnsShowFocus()
- 名詞性的通常沒有前綴:
- 用autoCompletion(), 不用isAutoCompletion()
- boundaryChecking()
有時候沒有前綴會產生誤導,在這種情就加上前綴is-:
- isOpenGLAvailable(), not openGL()
- isDialog(), not dialog()
(如果函數叫做dialog(),我們通常會認定它會返回QDialog*類型)
setter的命名可以從這推知,只要去掉is前綴,在名字前面加set前綴就可以了.比如setDown()和setScrollBarsEnabled().屬性的名字跟getter一樣,就是沒有is前綴
指針或引用?
對于向外傳參,是使用指針,還是引用更好呢?
void getHsv(int *h, int *s, int *v) const
void getHsv(int &h, int &s, int &v) const
絕大多數C++書籍都推薦無論何時都盡可能使用引用,因為從大多數情況來說,引用比指針有著所謂的"安全和優雅".相比而方,在Trolltech,我們更趨向于指針,因為它使用戶代碼更具可讀性.比較下面的代碼:
color.getHsv(&h, &s, &v);
color.getHsv(h, s, v);
只有第一行代碼能更清楚地說明h,s,v在函數被調用后,其值極有可能被修改.
案例分析:QProgressBar
為了在實際代碼中說明這些概念,我們以QProgressBar在Qt3和Qt4中的比較進行研究.在Qt 3中:
class QProgressBar : public QWidget
{
...
public:
int totalSteps() const;
int progress() const;
const QString &progressString() const;
bool percentageVisible() const;
void setPercentageVisible(bool);
void setCenterIndicator(bool on);
bool centerIndicator() const;
void setIndicatorFollowsStyle(bool);
bool indicatorFollowsStyle() const;
public slots:
void reset();
virtual void setTotalSteps(int totalSteps);
virtual void setProgress(int progress);
void setProgress(int progress, int totalSteps);
protected:
virtual bool setIndicator(QString &progressStr,
int progress,
int totalSteps);
...
};
對這個API進行改進的關鍵之處就是需要觀察到Qt 4中QProgressBar與QAbstractSpinBox,以及它的子類,QSpinBox,QSlider,和QDial有著相似性.解決的辦法呢?將其中的progress和totalSteps用minimun,maximum和value替換.
增加valueChanged()的信號量.增加setRange()這一方便的函數.
接下來需要到progressString, percentage 和indicator實際上都指代同一東西:顯示在進度欄上的文本.通常這一文本是一百分數,但是它能被setIndicator()設置成任何值.這里是新的API:
virtual QString text() const;
void setTextVisible(bool visible);
bool isTextVisible() const;
默認,這文本是百分比指示器.這可以用重新實現的text()進行改變.
在Qt 3中,setCenterIndicator() 和 setIndicatorFollowsStyle()是兩個影響對齊方式的函數.它們現在都被一個高級的函數所取代,setAlignment().
void setAlignment(Qt::Alignment alignment);
如果程序員沒有調用 setAlignment(),對齊是基于的樣式決定的.對于Motif樣式,文本顯示在中間,而對于其他樣式,文本是右對齊的.
這里是改進過的QProgressBar:
class QProgressBar : public QWidget32
{
...
public:
void setMinimum(int minimum);
int minimum() const;
void setMaximum(int maximum);
int maximum() const;
void setRange(int minimum, int maximum);
int value() const;
virtual QString text() const;
void setTextVisible(bool visible);
bool isTextVisible() const;
Qt::Alignment alignment() const;
void setAlignment(Qt::Alignment alignment);
public slots:
void reset();
void setValue(int value);
signals:
void valueChanged(int value);
...
};
怎樣寫出正確的APIs
APIs需要質量保證.最早的版本一般都不是很好的,你必須測試它.通過調用這個API的代碼作為測試事例,來驗證代碼具有可讀性.
另外的技巧包括讓人在沒有文檔和類文檔化(類的概述和函數說明)的情況下能夠使用這個API.
當你陷入麻煩中時,文檔化也是好的辦法找出一個合適的命名:試著為這些類,函數,枚舉值標住文檔,然后使用浮現在你腦中的第一個詞匯.如果你找不到精準的名字去表述,那很有可能這個東西就不應存在.如果任何辦法都失敗了,而且你確信這個概念是有用的,那就發明一個新的名字吧.最后,不管怎么說,"widget", "event", "focus", and "buddy"這些詞總會能用上一個.
posted @
2008-05-11 20:07 len 閱讀(7801) |
評論 (6) |
編輯 收藏
Glade是針對GTK+工具箱與GNOME桌面開發環境的快速圖形界面開發工具.用Glade設計的用戶接口以XML的文件形式保存,然后根據需要由程序通過libglade庫文件來動態加載.因為使用了libglade庫,Glade XML文件能夠被C,C++,Java,Perl,Python,C#等等語言所支持.針對其他未涉及的語言的支持也是方便的.
在網上可以見到某些關于Glade的教程,大都是關于Linux平臺和Glade 2的,因為原先Glade作為快速開發工具,集成代碼生成功能,生成C文件.所以常常有初學者對網上某些教程所提及的"generate"(生成代碼)功能表示迷惑,在新版本的Glade-3上找不到對應的功能.
新版本的Glade-3是對原先Glade代碼的完全重寫.一個顯著的變化就是去除了代碼生成功能.這樣做是有原因的,即然代碼生成功能不被提倡使用,而是更鼓勵使用libglade功能.但是如果你真需要代碼生成功能的話,它還是可以做為插件來提供的.另一個顯著的不同是glade-3設計用來最大化使用GObject的自省機制(GObject introspection),來使外部工具箱和部件的控制,信號和屬性的集成更加容易.
如果看過Say Hello to GTK+的話,可能感覺那樣的窗體程序太簡單了.那么現在讓我們借助Glade弄點兒復雜一點兒的界面吧.首先來瞧瞧Glade長什么樣,下圖就是Glade在windows下的界面.左邊的窗體的小部件選擇器,相當于調色板.中間是主菜單,右邊的是屬性窗體.
現在開始創建一個類似于文本編輯器的圖形界面.按照上圖標注的順序,依次添加window部件,vertical box部件,menu bar部件,text view部件和Status部件.vertical box設置三行,它是用來進行界面布局,分割空間用,這是gtk+設計與傳統的windows UI設計很不同的地方.后三個部件是放置vertical box中的,最后設計完成圖形如下.保存取名為win.glade.如果你感興趣的話,可以用文件編輯器打開這個文件看看,正如所說的那樣,它是一個xml格式的文本文件.
現在我們設置相關的頭文件和庫文件,編輯一個glade.c文件,添加進以下的代碼,運行看看,會出現如上圖的對話框.雖然這個對話框什么都不干,但是通過Glade,我們能較為容易地設計界面,而不用通過gtk函數,一個一個將控件實現.
#include <gtk/gtk.h>
#include <glade/glade.h>
int main(int argc, char* argv[])
{
GladeXML *gxml;
GtkWidget *window;
gtk_init (&argc, &argv);
gxml = glade_xml_new ("win.glade", NULL, NULL);
window = glade_xml_get_widget (gxml, "hello");
g_object_unref (G_OBJECT (gxml));
gtk_widget_show (window);
gtk_main ();
return 0;
}
posted @
2008-03-27 20:49 len 閱讀(10192) |
評論 (8) |
編輯 收藏