MongoLab提供的免費MongoDB服務

MongoDB是NoSQL資料庫中的佼佼者。如果您對NoSQL略有涉獵,應該也有聽過他的大名。如果你沒有時間自己假設一個MongoDB伺服器,但是又很想試用看看。MongoLab的新服務正是為你所準備的!

http://mongolab.com/

只要連到上面的網站,然後註冊一個帳號,就可以申請免費500MB的MongoDB服務。500MB看起來很多,依照應用的不同,或許很快就滿了。無論如何,作為試用也好,小型應用也好,有人免費幫你架設系統並且提供頻寬,不是很好嗎?

MongoLab本身提供了REST API讓你可以使用標準的方式來與其資料庫進行溝通,同時,它也針對幾個著名的雲端系統提供服務:

  • Amazon
  • Joyent
  • Rackspace
  • Windows Azure

他也提供了容易使用的Web介面的管理工具,然你可以透過瀏覽器就對你的資料庫進行操作。他還提供了Replication與backup服務,如此就不用擔心資料安全性的問題了!

在Qt中用OpenCV2透過Webcam拍照

最近在練習使用OpenCV,順手紀錄一些用法。下面這段code可以用來控制電腦上的webcam進行拍照並且轉換成QImage物件,以便後續的使用。

webcam.cpp
#include <opencv2/opencv.hpp>  
  
CvCapture *camera;  
IplImage *frame;  
  
camera = cvCreateCameraCapture(-1);     
  
/* We need to call cvRetrieveFrame twice here to get the last frame.  
 * TODO: figure out the root cause and use correct method instead of  
 *       the workaround.  
 */  
cvGrabFrame(camera);  
frame = cvRetrieveFrame(camera);  
frame = cvRetrieveFrame(camera);  
if (frame)  
{  
    QImage image(reinterpret_cast(frame->imageData),  frame->width, frame->height, frame->widthStep, QImage::Format_RGB888);  
    QImage colorCorrectedImage(image.rgbSwapped());  
    /* Show colorCorrectedImage which contains the picture. */  
    ...  
}  
cvReleaseCapture(&camera);  

如何建立可攜帶的Qt開發環境

本文將會向您展示如何建立一個Windows平台上面的可攜式Qt開發環境。本文是建立在Qt 5.0.1及MinGW 4.7之上。

事前準備

  1. 準備一個用來安裝Qt及MinGW的Windows系統。
  2. 從Qt project的網站下載qt-windows-opensource-5.0.1-mingw47_32-x86-offline.exe。

[][1]

製作步驟

  1. 使用qt-windows-opensource-5.0.1-mingw47_32-x86-offline.exe將Qt 5.0.1及MinGW 4.7安裝至電腦上。確定您在Select Components頁面上有選擇MinGW 4.7。本範例是將Qt安裝到C:\Qt的目錄裏。
  2. 將C:\Qt複製到您的USB碟上,我的USB碟是在E槽,所以就將c:\Qt複製到e:\Qt。
  3. 修改E:\Qt\Qt5.0.1\5.0.1\mingw47_32\bin\qtenv2.bat以便移除系統相依性。修改後如下所示:就是把磁碟機代號移除。 set PATH=\Qt\Qt5.0.1\5.0.1\mingw47_32\bin;\Qt\Qt5.0.1/Tools/MinGW\bin;%PATH% cd /D \Qt\Qt5.0.1\5.0.1\mingw4732_
  4. 將msvcp100.dll及msvcr100.dll複製到E:\Qt\Qt5.0.1\Tools\QtCreator\bin(要確認他們都是32-bits的版本) NOTE: 若您的系統是32-bit的,可以在C:\Windows\system32找到他們。NOTE: 若您的系統是64-bit的,可以在C:\Windows\SysWOW64找到他們。(C:\Windows\system32也有,但是那是64-bit的版本)

  5. 在E:\Qt目錄下建立一個qtcreator.bat的批次檔,其內容如下: start \Qt\Qt5.0.0\Tools\QtCreator\bin\qtcreator.exe -settingspath \Qt\Qt5.0.0\Tools\QtCreator\settings

  6. 執行qtcreator.bat,並等待QtCreator出現。

  7. 新增compiler(Tools->Options->Build & Run->Compilers), 按下新增按鈕並選擇MinGW。輸入下面的內容: Name: MinGW 4.7Compiler path: \Qt\Qt5.0.1\Tools\MinGW\bin\gcc.exe

    _

    _NOTE: 您需要使用瀏覽按鈕選擇gcc.exe的位置,接著手動將磁碟機代號移除。否則ABI選項無法被正確的決定。NOTE: 您必須選擇x86-windows-msys-pe-32bit作為ABI的設定。

  8. 建立一個名為qt.conf的檔案並且放在qmake.exe所在的位置(E:\Qt\Qt5.0.1\5.0.1\mingw47_32\bin),其內容如下: [Paths]Prefix = /Qt/Qt5.0.1/5.0.1/mingw4732_

NOTE: qt.conf內的目錄分隔字元必須是斜線,反斜線是不允許的。

  1. 新增Qt版本(Tools->Options->Qt Versions). 使用新增按鈕建立一個手動輸入的Qt版本手動選擇qmake.exe的位置,它應該位於E:\Qt\Qt5.0.1\5.0.1\mingw47_32\bin\qmake.exe。
  2. 新增Kit(Tools->Options->Kits). 使用新增按鈕建立新的kit. 您需要選擇前面步驟7及9所建立的Compiler及Qt version。同時,選擇E:\Qt\Qt5.0.1\Tools\MinGW\bin\gdb.exe為Debugger。
  3. 前面步驟完成後,關閉QtCreator。
  4. 進入E:\Qt\Qt5.0.0\Tools\QtCreator\settings\QtProject\qtcreator
  5. 修改qtversion.xml

  6. 在qtversion.xml中,找到下面設定: E:/Qt/Qt5.0.1/5.0.1/mingw47_32/bin/qmake.exe

  7. 將QMakePath設定中的磁碟機字元移除: /Qt/Qt5.0.1/5.0.1/mingw4732/bin/qmake.exe_

  8. 修改profiles.xml

  9. 在profiles.xml中找到下面的設定 E:\Qt\Qt5.0.1\Tools\MinGW\bin\gdb.exe

  10. 將Binary設定中的磁碟機字元移除 \Qt\Qt5.0.1\Tools\MinGW\bin\gdb.exe

  11. 現在將你的USB碟移到其他沒有安裝過Qt的電腦上面,執行qtcreator.bat來啟動QtCreator。

  12. 試著建立一個簡單的專案來看看你的環境是否OK。

NOTE: 如果你看到Failed to load platform plugin “windows”這樣的錯誤。將步驟8所建立的qt.conf放到你的程式目錄下,就可解決此問題。舉例來說,若您的程式輸出是在d:\src\myapp\release,就將qt.conf複製到此目錄下。

[1]:

讓Sublime text按下tab後可以自動跳到匹配的括號後面

Sublime text的自動括號匹配的功能很方便也很實用,但是在輸入完括號內的字卻還要按幾次右鍵才能到括號的後面去繼續編輯就顯得有點美中不足。所幸,藉由sublime text本身強大的設定功能,只需要將下面的設定加入到Preference->Key Binding - User後,就可以按下tab快速地跳到括號後面繼續編輯了。

[][1]

[{ "keys": ["tab"], "command": "move", "args": {"by": "characters", "forward": true}, "context":  
 { "key": "following_text", "operator": "regex_contains", "operand": "^[)\"\\]]", "match_all": true },  
 { "key": "auto_complete_visible", "operator": "equal", "operand": false }  
 ]  
}]  
[參考來源][2]

[1]:

將一個多層的list或是tuple解開並且攤平

有些時候我們會需要將一個有很多層的list解開,變成一維的list。其實三行程式碼就可以解決這個問題。只要你懂得如何使用reduce這個工具。

[][1]

#!/usr/bin/env python  

import collections  

flatten = lambda p, c : p+reduce(flatten,c,[]) if isinstance(c, collections.Iterable) else p+[c,]  

nested_list = [1, 2, 3, 4, 5, 6, [7, 8, [9, 10], [11, [12], 13]], 14, [15, 16, [17, [18, [19, 20], 21], 22, 23], 24, 25]]  
print flatten([], nested_list)  

輸出結果如下:

[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 
22, 23, 24, 25]

[1]:

狀態機的程式設計風格

本文是要說明狀態機程式的寫法,如果你曾經看過狀態機或是已經知道怎麼樣畫狀態機,但是卻不知道怎麼樣寫程式,那麼本文將會讓你知道怎麼做。

一個狀態機包含了四個元素

  1. 狀態(state)
  2. 轉移條件(transition condition),也有人用事件(event)或是訊息(message)來表示。
  3. 輸出(output),也有人用工作(task)表示。
  4. 輸入(input)

其中輸入這項其實是非必要的。

[][1]

考慮一個紅綠燈的控制一個程式,讓我們先描述紅綠燈的工作方法

  1. 綠燈 60 秒後,轉黃燈。黃燈 10 秒後,再轉紅燈。
  2. 紅燈 40 秒後,轉黃燈。黃燈 5 秒後,再轉綠燈。

首先,我們要找出所謂的狀態。在問題的描述中,狀態多半是以名詞或是形容詞的形式出現的。所以,上面的描述中,名詞有哪些?

紅燈, 黃燈, 綠燈剛好就是最明顯的三個名詞。讓我們想想看,這三個名詞是合適的狀態代表嗎?

一個好的狀態

  1. 清楚的定義。通常,不清楚的定義代表狀態內可能複合了好幾個狀態,試著把他們分離出來形成獨立的狀態,就會清楚了。
  2. 明確的轉移條件。過於複雜的轉移條件,可能也是因為複合了好幾個狀態的結果。分離他們,條件就會清楚了。
  3. 彼此間很容易找到關係。亦即若你可以找到一條轉移的路徑,從A狀態直接或間接轉移到B狀態。這兩個狀態就是有關係的。若是一個狀態或是一組狀態與其他的狀態完全沒有交集或關係。那麼你面對的可能不是單一的狀態機。把他們分成兩個分開寫吧。

毫無疑問的,紅燈, 黃燈, 綠燈一定是狀態,我們需要給狀態一個ID,好方便我們寫成構思狀態機及寫程式。 習慣上,我們會用enum來宣告state。

enum STATE {STATE_RED, STATE_YELLOW, STATE_GREEN};  

那麼轉移條件呢?轉移條件通常帶有邏輯描述。具有比較限制大小長短前後轉換等詞彙或是相似的語句。
此外,在這些語句或詞彙的前後往往就是前面所找出的狀態。

在前面的描述中,符合上面所說的有

  1. 綠燈 60 秒後,轉黃燈。
  2. 黃燈 10 秒後,再轉紅燈。
  3. 紅燈 40 秒後,轉黃燈。
  4. 黃燈 5 秒後,再轉綠燈。

讓我們用比較接近程式的說法,重新撰寫這四個敘述。

  1. 若在綠燈狀態60秒後,切換到黃燈狀態。
  2. 若之前是由綠燈轉到黃燈狀態,且在黃燈狀態超過10秒後,切換到紅燈狀態。
  3. 若在紅燈狀態40秒後,切換到黃燈狀態。
  4. 若之前是由紅燈轉到黃燈狀態,且在黃燈狀態超過5秒後,切換到綠燈狀態。

要注意的是第二及第四點,雖然原來的敘述沒有特別說明前面一個狀態與狀態轉移之間的關係。但是,其中『再』這個字卻是暗中點出了這點。 在處理狀態轉移條件關係時,這種與前後文有關的敘述要特別注意。

另外是輸入與輸出的部份。雖然描述中沒有特別提及什麼使用者輸入或是其他控制訊號的輸入。實際上,如果我們要做有關時間相關的控制時, 免不了需要有timer或是counter這類的輸入來做為輔助。

而輸出的部份就比較容易理解了,對燈號的控制就是我們的輸出。

/* Define states. */  
enum STATE {STATE_RED, STATE_YELLOW, STATE_GREEN};  

/* State flags are declared here. */  
STATE state_before_change;  
STATE state_prev;  
STATE state;  
bool state_changing;  

/* The threshold or conditions for state machine are defined here. */  
const int period_red = 40;  
const int period_green = 60;  
const int period_yellow_green_to_red = 10;  
const int period_yellow_red_to_green = 5;  

/* Reset state machine to initial state. */  
void reset_state_machine()  
{  
 state_before_change = STATE_RED;  
 state_prev = STATE_RED;  
 state = STATE_RED;  
 state_changing = false;   
}  

/* Execute state machine. reset_state_machine()   
 * should be called before the first time run_state_machine called().   
 */  
void run_state_machine()  
{  
 /* State transition */  
 if (state_prev == STATE_RED)  
 {  
 if (timer >= period_red)  
 {  
 state = STATE_YELLOW;  
 }  
 }  
 else if (state_prev == STATE_YELLOW)  
 {  
 if (state_before_change == STATE_RED && timer >= period_yellow_red_to_green)  
 {  
 state = STATE_GREEN;  
 }  
 else if (state_before_change == STATE_GREEN && timer >= period_yellow_green_to_red)  
 {  
 state = STATE_RED;  
 }  
 }  
 else if (state_prev == STATE_GREEN)  
 {  
 if (timer >= period_green)  
 {  
 state = STATE_YELLOW;  
 }  
 }  
 else  
 {  
 // Non-exist state.  
 ASSERT(FALSE);  
 }  

 /* Conditions and flags for state transition. */  
 state_changing = (state != state_prev);  
 state_before_change = (state_changing) ? state_prev : state_before_change;  
 state_prev = state;  

 /* Tasks which are independent from states. */  
 if (state_changing)  
 {  
 RESET_TIMER();  
 }  

 /* Tasks which are related to specified state.*/  
 if (state == STATE_RED)  
 {  
 LED_RED(ON);  
 LED_YELLOW(OFF);  
 LED_GREEN(OFF);  
 }  
 else if (state == STATE_YELLOW)  
 {  
 LED_RED(OFF);  
 LED_YELLOW(ON);  
 LED_GREEN(OFF);  
 }  
 else if (state == STATE_GREEN)  
 {  
 LED_RED(OFF);  
 LED_YELLOW(ON);  
 LED_GREEN(OFF);  
 }  

 /* Tasks which are independant from states. */  
 // DO SOMETHING.  
 } /* void run_state_machine() */  

這個程式主要分為兩大區塊

  1. 狀態轉移控制區塊
  2. 輸出控制區塊

其中,輸出控制區塊按順序又可細分為:

  1. 狀態無關前級輸出控制區塊
  2. 狀態相依輸出控制區塊
  3. 狀態無關後級輸出控制區塊

其中,狀態相依輸出控制區塊很好理解,就是依照現在的狀態所需控制的動作。這個區塊的 程式第一個要做的是判斷目前的狀態,然後去做對應的輸出控制。

至於位於狀態相依輸出控制區塊之前與之後的兩個控制區塊,主要是肩負輸出控制區塊的準備與收尾的工作。 這兩個區塊其實是可以分散在不同的狀態輸出控制條件中。但是,常會發現同樣的與狀態無關的工作將會重複出現在狀態控制中。
為了節省程式碼,並且減少程式碼拷貝的情形。將其獨立於狀態控制的前後將會是實作上很好的作法。 以上面的例子來說,如果我們將RESET_TIMER()寫在不同的狀態輸出控制中,則每個狀態都要寫一次。程式會變成下面這樣。

...  

/* Tasks which are related to specified state.*/  
if (state == STATE_RED)  
{  
 /* Tasks which are independant from states. */  
 if (state_changing)  
 {  
 RESET_TIMER();  
 }  

 LED_RED(ON);  
 LED_YELLOW(OFF);  
 LED_GREEN(OFF);  
}  
else if (state == STATE_YELLOW)  
{  
 /* Tasks which are independant from states. */  
 if (state_changing)  
 {  
 RESET_TIMER();  
 }  

 LED_RED(OFF);  
 LED_YELLOW(ON);  
 LED_GREEN(OFF);  
}  
else if (state == STATE_GREEN)  
{  
 /* Tasks which are independant from states. */  
 if (state_changing)  
 {  
 RESET_TIMER();  
 }  

 LED_RED(OFF);  
 LED_YELLOW(ON);  
 LED_GREEN(OFF);  
}  

這還是因為我們的狀態少。假設有N個狀態,就會有N-1份的程式碼要寫。至於前後級之分, 主要是看狀態無關的工作是必須在狀態相關的工作做之前還是做完後,才能進行。通常屬於準備
性質的工作會放在前面,收尾性質的工作在後面。

最後,state_changing, state_before_change及state_prev這三個條件幾乎是狀態機都會用上的 條件,不妨在一開始設計狀態機時就寫上去。這三個狀態條件使用的時機為:

state_changing用來表示現在正是狀態轉換的瞬間。需要在狀態變化時做的事情可以利用這個條件來做。state_before_change這是用來記錄狀態變化之前的狀態。用在我們需要清楚知道我們目前狀態是由哪個狀態變化而來。state_prev單純用來記錄前一個狀態,不管是否狀態有變化過。

上面的範例中已經點出了一個狀態機程式的基本結構及樣式:

  1. 宣告狀態為常數,並給予符合其意義的名稱。
  2. 撰寫狀態轉移控制條件。
  3. 填入狀態轉移旗標(state_changing, state_before_change, state_prev)
  4. 執行狀態工作的準備程序 (狀態無關前級輸出控制區塊)。
  5. 執行狀態工作 (狀態相依輸出控制區塊)。
  6. 執行狀態工作的結束程序 (狀態無關後級輸出控制區塊)。

狀態機一般都是會被引入在一個持續執行的迴圈中。所以呼叫狀態機的程式可能如下:

void main()  
{  
 reset_state_machine();  

 while (1)  
 {  
 run_state_machine();  
 sleep(1000); /* Call sleep() to avoid this loop occupy too much CPU time. */  
 }  
}  

狀態機不是唯一描述邏輯的方法,但是卻是相當優雅的一種。尤其當你的條件多到無法處理的時候,狀態機可以幫你分類你的條件。 讓你只專心於某個狀態到另一個狀態之間的條件處理。

程式設計師應該將狀態機視為必備的技能之一。善加運用在工作之中,以期寫出可讀性更高的程式。

ps: 前面的範例中,多用if-elseif-else作為狀態的判斷。其實,還蠻多人使用switch作為狀態的判斷,這兩者其實是相同的東西。

[1]:

Qt終於要逃離Nokia這條正在下沉的船了

http://www.digia.com/en/Home/Company/News/Digia-to-acquire-Qt-from-Nokia/

http://blog.qt.nokia.com/2012/08/09/investment-in-qt-planned-to-continue-digia/

今天看到了上面的兩個Link。是的!Qt終於要脫離Nokia了。Nokia目前的處境大家都知道整個是處於一個下沉的趨勢。是會在不久的將來撐不下去而賣掉,還是會因為聯合了Microsoft陣營而東山再起。我們不得而知。但是,對於Qt的愛好者來說,Qt在Nokia中已經註定沒有未來是很肯定的事情。

[][4]我想應該是內部的Qt相關人士的積極奔走吧。終於,在Digia的表態下,Qt至少可以先脫離Nokia這艘正在下沉的船了。Digia這間公司之前就是在負責Qt有關商業授權的業務。現在,為了讓這個業務能夠繼續下去,買下Qt也是可以理解的決定。而且,之前的link內也有說到Nokia內有一部分的Qt人員也會轉移過去Digia。

目前就看這陣子轉移的情況了。目前看起來Qt的一切開發並沒有因為Nokia或是此次的轉移而有所停頓。未來也仍將朝Qt/QML的方向走下去。雖然,還沒有看到的什麼光明的未來,但是,至少看起來還是個有希望的未來。讓我們這些Qt的愛好者們祈禱這次的轉移能夠順利成功,讓Qt可以更健康的成長,並且持續的走下去!

UPDATE(2012/09/18): 今天Digia宣布完成了Qt的併購。終於,Qt可以離開風風雨雨的Nokia!未來是好是壞,讓我們繼續看下去!

[4]:

如何在開機的時候顯示Windows 8的進階啟動選項

有些時候你會需要能夠在開機的時候選擇不同的開機選項(Advanced Startup options)。Windows 7以前,你只要在開機的時候按F8按的夠快就可以了。但是Windows
8呢?

其實,Windows 8也是有這個選項的。但是,你會發現不管你怎麼按F8都叫不出他了。原因是Microsoft為了讓開機的時間更短,預設把這個查看F8的動作給關了。因此,我們要進行一些設定將這個選單給打開來。

[][1]作法如下:

  1. 在具有管理者權限的命令列工具中輸入下面的指令。
      > bcdedit /set {bootmgr} displaybootmenu yes
  2. 重新開機
  3. 現在你會看到開機過程停在一個純文字的選單畫面。現在你可以慢慢的按F8來啟動進階開機選項的選單了。
  4. 按下F8後你就會看到這個久違的選單了。現在就可以挑選你要的開機選項了。

[1]: