2017年4月4日 星期二

改用影片教學 這裡以後放草稿

重新置頂刷新

由於我有範例的文章都有舉實例,程式碼加上描述再加上圖真的太多了

因此改用影片來講,這樣示範也比較完整

基本上影片就是邊打邊講解了,您會看到比較原始的講解方式,也就是不完美的教學方式,教學時過程會出錯,

其實這樣也可以順便學習Debug,並且了解人家如何Coding

不然會累死我TOT

這篇以後您看到的都會是草稿,就是我將草稿上傳(圖太多,弄成文章太累了,順便騙點閱率@@,雖然很多點閱率看起來很像是機器灌水,不過看到數字在跳蠻爽的\^O^/)

舊的文章有需要的我也會找時間弄成影片,

總之可以用文字描述的就用文字寫,如果影片講解比較快的改用影片方式(目前沒工作中等有工作再來搞)

至於影片會整理在hahaha C++教學區
這裡改成變草稿區了

新的網誌
http://hahaha-cplus.blogspot.tw

關於XE系列新版元件字串儲存類型

這裡來講個基本的
大家都知道C++裡面儲存字串的東西是
Std::string(單字元)和Std::wstring(寬字元)
那C++ Builder裡面則是

AnsiString(單字元)和UnicodeString(寬字元)

基本上我習慣用C++裡面的變數,而不用C++ Builder裡面內建的變數(UnicodeString),原因在於我寫的程式是多執行緒程式,C++ Builder裡面一些底層都藏起來了,網路上也沒說他是不是thread safe

因此以防萬一,原則上有多執行緒的程式片段,盡量不要使用C++ Builder內建的型態,不是很保險,當然有些是thread safe但是C++ Builder其實他也沒講,程式碼怎做的也不知道,總之就是不保險

但其實這裡寫到UnicodeString應該是thread safe的
http://www.codenewsfast.com/cnf/thread/0/permalink.thr-ng1877q16781

至於那四個東西怎麼使用,這裡不講,其實是有辦法互相轉換的
例如AnsiStriing和UnicodeString可以直接轉,他會自動在單字元和寬字元做轉換
http://bbs.csdn.net/topics/370062441

UnicodeString US = L“test”;
AnsiString AS = “abc”;
US = UnicodeString(AS);
AS = AnsiString(US);


上面是可行的,但是您要知道要是寬字元有存一些外國字(例如日文或韓文),轉成單字元會出現亂碼,除非您把系統的字碼切換到該國語言的狀態下,但是中文就會變成亂碼了

另外
就我在XE7時試
Edit1->Text = “XXX”;
好像不行,因為他要UnicodeString(寬字元)

可是到XE10 berlin,竟然可以,會自動判斷
看起來還是有再精進

因此我原本要說他不行的(因為我XE7時印象中不行),現在新版好像都可以了
UnicodeString US = “test”;
他也會自動幫您轉成功

所以看起來這已經不是技巧了Orz

總之他需求類型是要UnicodeString這個,但是由於XE10 berlin版有再做精進

因此下面的語法會過
std::string fffe= "fsdfsd";
UnicodeString fff = fffe.c_str();
Caption = fffe.c_str();


所以請大家一起學習吧Orz,我剛看了才發現現在竟然已經可以了,比wstring還好用

但是您會問為什麼不用string呢,就是前面講的,string存一些日文或韓文會顯示亂碼,wstring和UnicodeString不會

其實有另一個原因是由於C++ Builder底層是Delphi做的,您只要Inlcude<vcl.h>或是相關的
#include <System.Classes.hpp>
#include <Vcl.Controls.hpp>
#include <Vcl.StdCtrls.hpp>
#include <Vcl.Forms.hpp>

…等

編譯速度會很慢(但是您要使用UnicodeString又要include<vcl.h>)

所以還是等C++ Builder改善編譯速度後再決定要不要使用C++ Builder的類型吧

按鈕效果快速處理方法Array

在之前關於新版移植及Skin及自製按鈕效果,提到了如何讓Panel具有簡單效果,一共有三種如下:
1. Button
2. Down Button
3. Pic Button


並且我們可以利用函式設定按鈕的顏色

我提供的class可能會執行時出錯,所以細部的防呆需要您自行的處理,原則上您只要利用建構元給予效果特性,並把建構元(Constructure)存起來,他效果就不會消失(因為我在解構元時將關聯刪除,要是註解調解構元(Destructure),他就可以直接下建構元產生效果),當然我裡面有動態建立Timer,假設沒delete掉會memory leak,所以還是不要那樣做

在這邊提醒一下,我的Down Button您可以傳一個bool*進去,他會變成利用您的bool變數判斷是否按下,不知道上次有沒有講,忘了,應該是有講,在這邊再提醒一次

這有什麼好處呢,也就是要是您要把按鈕狀況存下來,您可以將bool*傳進去,他就會把按下的狀況存在您傳進去的bool裡面,那要是您要同步參數檔,就把參數檔裡面的bool變數傳給他,然後您要存時就存該變數即可,不需要去讀效果物件裡面的Check_t_

前言講完
這次來講我要如何快速地利用列表方式給予按鈕們特效,想想我們可以做出效果了,是否可以利用這個效果做出一個StatusBar,或是系統Menu呢?

當然是可以的,其實就寫一個Array做一個列表,將您要做效果的所有按鈕元件都填入這個Array裡,利用簡單的for loop將這些東西一個一個存入效果cllass的vector裡面,這樣他們就完全存住了

那要是要全部變換效果,也很簡單,您只需要寫一個for loop,用我效果class裡面寫好的Setting_Color設定他們各種狀況下的顏色,就可以用最短的code做一次性更新

這邊我就順便也示範TColorDialog的用法,這樣您就可以選您喜歡的顏色直接切換
基本上就打開我的連結看專案裡面的Code了,我這邊就不再貼上,因為Blogger其實排版挺累的

我用上次的關於新版移植及Skin及自製按鈕效果專案改的,
這次多一個簡單的Menu,其中Panel按鈕要一個一個排,其實很累,所以我也是有指令的方式排列,這是一種技巧,可以幫助您寫Code省力

基本上我有提供排序的按鈕,您也可以將註解打開它就會排好了,並可以程式化設定按鈕與邊界的間隔

並且我的Menu可以有Sub Menu,基本上我只做兩層,第三層作法依此類推,總之就自己想想吧
我在裡面會順便小用一下auto,有興趣可上網查auto c++11 :D

由於是自己做的,您無聊放一些圖案在上面也是可以的,只是點選部分要再處理,基本上要改效果class,如您不介意被干擾,就直接放圖吧(我有做示範,有一顆按鈕點圖沒反應)

那些效果可以切換,只要將一個Array裡的物件放到另一個Array,並且格式相符,按鈕也可以變成,按鈕(Down)

附帶一提,要移除只要將vector清空即可,效果就會消失,我會提供一個按鈕給您們按按看
別忘了 我的按鈕也可以利用ColorDialog換顏色(我簡單做而已,其實可以做6個按鈕來設定)

至於出現的Sub Menu我使用Timer將他定時關閉(如果checkbox1有勾選的話),也是一種技巧,基本上後期要用Hook去監視滑鼠事件,來關閉Sub Menu,這裡就先暫時不講了,那算比較深的技巧了,有興趣請先自己上網查

您會看到裡面有很多Array的用法,他可以使您更方便寫Code,而不用一行一行的下指令變換
裡面有很多技巧的(我放入5~6個技巧),有興趣研究一下吧XD
以後再來教Radio Button作法,很類似的,就是多個選一個,只有一個會按下

範例連結:
https://drive.google.com/open?id=0B6u5oifJv3EKY19Hc2xWcl92Z2s

Windows消息傳遞

http://blog.xuite.net/grimmslaw/78/53174061-SendMessage()+%E8%88%87+PostMessage()

在C++就是使用PostMessage和SendMessage
而在C#其實有另一種委派(delegate),這方面我就不熟了

所以我這邊只講PostMessage和SendMessage

基本上現代的程式都是多執行緒程式,一般在做處理時,要將運算比較多的部分移到背景執行緒去做,最後再通知主執行緒處理畫面UI的動作,這時候就會用到PostMessage和SendMessage,

當然您也可以用Timer監控一些flag做一些UI更新,但是您想想,假設我30ms觸發Timer檢查一次UI要不要更新,那1秒鐘要檢查30次,要是您要檢查很多總類的UI更新,那不就要判斷很多flag例如下面這樣

void __fastcall TForm1::Timer1Timer(TObject *Sender)
{
If(flag1)
{
// 更新某部分UI
}
If(flag2)
{
// 更新某部分UI
}
If(flag3)
{
// 更新某部分UI
}
}


這樣有壞處,就是您Timer要一直跑,再來就是您處理這麼多flag更新畫面不累嗎
因此後來就有所謂的PostMessage和SendMessage,通知主Form的WndProc處理畫面

如連結講得的
PostMessage通知完主Form處理會馬上return,當主畫面有空時會去處理接收到的消息(PostMessage一次,會處理一次)
SendMessage則是會Block住,等到送出去的消息處理完,才會執行下一行


基本上意思就是thread做工作,要處理UI時再通知主Form處理,只需要一次的動作,也比較直觀,並且不需要寫一大堆flag做同步動作

當然了thread的處理會有同步的問題要解決,基本上要先知道Critical Section或mutex來處理同步問題,礙於篇幅,這裡先不講,往後會再補充詳細

總之意思大概就是假設您將影像存在一個陣列裡,要是這時您通知主Form處理將影像陣列畫到畫面上,結果您的執行緒在while loop裡又重新將影像陣列重新new,這時您可能剛好再畫影像,結果影像陣列剛好重新new時位置換地方了,導致出錯

這種細節很討厭的,要做很多同步處理,只要沒處理好就會當機,程式根本就不能用
好啦,總之會有同步問題往後有機會會講清楚
這裡您可以參考我的範例裡面註解的地方做建置,您大概就知道用法了,

總之我會有幾個Button,您點點看,照我說的地方下中斷點,追Code看看,您就知道了
這裡就不打那麼多了,請看我的範例就好

總之Wndproc是Windows程式用來接收訊息的地方,每個元件都有一個Wndproc,但是基於方便,我們統一送給主Form的Wndproc,而不是到處送給底下的vcl元件,避免太亂

用法舉PostMessage的例子(SendMessage一樣,只是效果不同)
PostMessage(Handle, WM_EVENT, event, (DWORD)data);
其中Handle是您要處理的主Form
WM_EVENT是自訂義的東西,用來識別您要接收的事件

#define WM_EVENT WM_USER + 10000

Event就是您的事件放在lparam,data,放您要送的變數或指標放在rparam,用以傳遞訊息
總之送過去,您只要照我這樣寫,對應的處理函式會被觸發,您就可以在那邊處理UI的東西了

我這只提供MESSAGE_MAP法,其實您可以virtual繼承Wndproc來觸發您要執行的事件,意思是相同的

這裡要提到的是,照理講,我裡面Button7在執行緒執行UI操作,應該會當掉的,不知道為什麼在XE10沒當…

其實在執行緒執行UI不是每次都會當的,他是有機會當掉,例如兩個執行緒同時存取相同VCL元件,

我範例裡面有6個按鈕5~10,您如果隨便點不點7,10,影像顯示正常,

要是您隨便點的話,例如7,9快速點(先把我連續post_event註解打開,讓他比較容易打架,不然您改成兩個執行緒,用while loop一直畫),過一陣子TImage顯示會異常,

也就是兩個執行緒不能同步更新TImage的,還是乖乖通知主Form更新吧

範例連結
https://drive.google.com/open?id=0B6u5oifJv3EKV184Smh3SFdYelU






template實例化和typedef用法

http://stackoverflow.com/questions/115703/storing-c-template-function-definitions-in-a-cpp-file
http://www.codeproject.com/Articles/48575/How-to-define-a-template-class-in-a-h-file-and-imp
主要是參考上面兩篇,其中第二篇的方法3才是我要用的方法

先說想法,我們知道template可以省掉非常多的寫Code量,舉例來說,我們生成一個不同類型的Point,也就是XY值,假設您要生成有char類型的,short int類型的,int類型的或是double類型的結構,

您看這樣就要寫四份相似的class,替每個結構弄一個class,新增一個function要改每份一次,修改一個function每份也要調一次,真的會累死

註:至於點為什麼要那麼多類型,因為您會發現vcl元件mouse move時的XY是int,但是您會發現您在做影像處理運算時,點的類型要用double,因為您要直接用點做運算

這樣其實Coding量會很大,因此使用Template可以減少Coding量,上面的問題您只要寫一個template class就可以完成了,看這個之前,我當作您已經知道template再幹嘛了,因為我這裡不是要講template怎麼用,而是要講其他問題

如此使用template可以減少工作量,這不是很好嗎,但是衍伸問題

Template實作只能寫在.h中,這會造成我們要是要修改template的東西時,會一直修改.h檔,導致編譯時有include到這個檔案的會跟著重新編譯,導致編譯時間變長,要是您這個東西很常用,您會發現您常常要要同時編譯很多.cpp檔,程式才可以Link

這不是一個好做法,因此我們就會想有沒有辦法能將實作的地方改到.cpp檔呢

其實就是template實例化(template instantiation)

假設我們做出template,這是一般寫法
point.h
template <typename T>
struct hahaha_point
{
public:
T x_;
T y_;
public:
hahaha_point(){}
~hahaha_point(){}
Void function(){};
};


您會發現上面的函式都必須在.h修改,不然compiler完會Link不到東西,例如下面這樣
point.h
template <typename T>
struct hahaha_point
{
public:
T x_;
T y_;
public:
hahaha_point();
~hahaha_point();
Void function();
};

Point.cpp
template <typename T>
hahaha_point<T>::hahaha_point()
{
x_ = 0;
y_ = 0;
}

template <typename T>
hahaha_point<T>::~hahaha_point()
{
}

template <typename T>
void hahaha_point<T>::function()
{
}


當您有使用到上面的函數時,例如function,會找不到function(unresolved external)
因此我們在.cpp最後面加入下面(記得一定要寫在同一個.cpp,不然一樣unresolved external)
template class hahaha_point<int>;
template class hahaha_point<float>;
template class hahaha_point<double>;


這樣就完成了,往後您在使用時,如下

Hahaha_point<int> a;
a.function();
就不會發生unresolved external的問題了

而且最重要的是,您要修改實作內容,例如改function的內容,只要在.cpp更改,會比較方便,不會案子做大了一直編譯一直改,很累的:D

附帶一提,為了方便可以將template的類型在.h定義
typedef hahaha_point<int> __POINT_32S;
typedef hahaha_point<float> __POINT_32F;
typedef hahaha_point<double> __POINT_64F;

typedef __POINT_32S POINT_I;
typedef __POINT_32F POINT_F;
typedef __POINT_64F POINT_D;


這樣往後您就可以用比較短的字做宣告,例如
POINT_I pt;
Pt.function();

方便吧,

雖然您使用一種新類型要template實例化一種,但是只需做一次,往後改時還是比較方便的