轉(zhuǎn)帖|其它|編輯:郝浩|2010-08-13 12:15:49.000|閱讀 846 次
概述:本文適用于 GUI 開(kāi)發(fā)人員,這些開(kāi)發(fā)人員要編寫(xiě)可移植、可重用和速度更快的控件,用于看到量大且復(fù)雜的數(shù)據(jù)。當(dāng)前存在一些常見(jiàn)的問(wèn)題,如性能差,還存在一些可用性問(wèn)題,如不能清楚地顯示大型數(shù)據(jù)集,所以用戶可以很容易地通過(guò)瀏覽本文進(jìn)行分析。另外,程序數(shù)據(jù)結(jié)構(gòu)和可視數(shù)據(jù)表示彼此之間的依賴性通常也變得非常強(qiáng)。因此,控件的專(zhuān)用性變得非常強(qiáng),如果不進(jìn)行重要修改,就不能在其他應(yīng)用程序中使用。本文提供了一種方法,可用來(lái)設(shè)計(jì)復(fù)雜的控件,解決以上討論的問(wèn)題。
# 界面/圖表報(bào)表/文檔/IDE等千款熱門(mén)軟控件火熱銷(xiāo)售中 >>
本文適用于 GUI 開(kāi)發(fā)人員,這些開(kāi)發(fā)人員要編寫(xiě)可移植、可重用和速度更快的控件,用于看到量大且復(fù)雜的數(shù)據(jù)。當(dāng)前存在一些常見(jiàn)的問(wèn)題,如性能差,還存在一些可用性問(wèn)題,如不能清楚地顯示大型數(shù)據(jù)集,所以用戶可以很容易地通過(guò)瀏覽本文進(jìn)行分析。另外,程序數(shù)據(jù)結(jié)構(gòu)和可視數(shù)據(jù)表示彼此之間的依賴性通常也變得非常強(qiáng)。因此,控件的專(zhuān)用性變得非常強(qiáng),如果不進(jìn)行重要修改,就不能在其他應(yīng)用程序中使用。本文提供了一種方法,可用來(lái)設(shè)計(jì)復(fù)雜的控件,解決以上討論的問(wèn)題。本文中,將使用圖表查看器控件的一些示例來(lái)說(shuō)明基本概念。這些概念還可以適用于多種多樣的其他控件。
定義
圖表代表一組對(duì)象及對(duì)象之間的關(guān)系。對(duì)象叫做節(jié)點(diǎn)。節(jié)點(diǎn)之間的關(guān)系叫做邊。因此,一個(gè)可視圖表就是一組節(jié)點(diǎn)(有或沒(méi)有標(biāo)簽的正方形、長(zhǎng)方形、圓等)和連接節(jié)點(diǎn)的邊(直線或曲線)。定義節(jié)點(diǎn)與邊的位置的算法叫做布局。
請(qǐng)注意,節(jié)點(diǎn)中可以包含其他節(jié)點(diǎn)和邊(子圖表)。如果某些節(jié)點(diǎn)的邊從這些節(jié)點(diǎn)起連接到任一個(gè)給定節(jié)點(diǎn),那么這些節(jié)點(diǎn)叫做這個(gè)給定節(jié)點(diǎn)的父節(jié)點(diǎn)。如果某些節(jié)點(diǎn)的邊從任一個(gè)給定節(jié)點(diǎn)起連接到這些節(jié)點(diǎn),那么這些節(jié)點(diǎn)叫做這個(gè)給定節(jié)點(diǎn)的子節(jié)點(diǎn)。
圖表控件中的常見(jiàn)問(wèn)題
在很多應(yīng)用程序中都使用圖表控件來(lái)顯示數(shù)據(jù)。如果要在不同的應(yīng)用程序中使用相同的圖表控件,就必須提供一種方法來(lái)自定義該圖表控件。這種自定義一定不能影響應(yīng)用程序的性能。以下是在不同應(yīng)用程序中應(yīng)用控件時(shí)可能遇到的各種問(wèn)題列表:
圖表控件上必須提供滾動(dòng)和縮放工具才能瀏覽大型圖表。下一部分說(shuō)明如何靈活地解決這個(gè)問(wèn)題而不降低任何性能。
自定義的外觀和用戶交互
通常您可以區(qū)別控件的兩個(gè)部分,即區(qū)別數(shù)據(jù)元素與核心部分,數(shù)據(jù)元素表示數(shù)據(jù)的各個(gè)部分,核心部分負(fù)責(zé)將數(shù)據(jù)組織為一個(gè)整體。圖表數(shù)據(jù)元素由節(jié)點(diǎn)和邊組成。當(dāng)需要時(shí),核心部分會(huì)使用數(shù)據(jù)元素的自定義版本來(lái)提供一些功能,如滾動(dòng)、縮放、繪圖和事件處理。例如,當(dāng)您單擊鼠標(biāo)按鈕時(shí),核心部分會(huì)定義這個(gè)事件發(fā)生的位置。如果事件發(fā)生在某個(gè)數(shù)據(jù)元素上,那么事件信息會(huì)傳遞到這個(gè)數(shù)據(jù)元素的處理程序,此外核心部分會(huì)處理事件本身。
圖表中有兩樣?xùn)|西項(xiàng)目會(huì)隨著不同應(yīng)用程序而改變,您應(yīng)當(dāng)進(jìn)行自定義。一個(gè)是數(shù)據(jù)元素的外觀與行為;另一個(gè)是組織元素的方式。如果要很容易地自定義控件,就需要為這些東西定義界面,然后僅通過(guò)界面將這些東西用于控件。所以,如果要進(jìn)行某種更改,只需要采用新的方式實(shí)現(xiàn)界面,無(wú)需更改控件中的代碼。這就是所謂的“策略”模式。
圖表控件采用了以下策略:
class INodeHandler
{
public:
// 繪制給定的節(jié)點(diǎn)
virtual void Draw(Node) = 0;
// 返回描述正方形的尺寸。此函數(shù)在布局中用來(lái)確定
// 節(jié)點(diǎn)位置,沒(méi)有交叉點(diǎn)。
virtual idvc::dsize GetSize(Node)= 0;
// 設(shè)置所有后續(xù) Draw 調(diào)用函數(shù)中要使用的縮放系數(shù)。此函數(shù)
// 在實(shí)現(xiàn)縮放時(shí)由控件的核心部分使用。
virtual void SetZoomFactor(double f) {};
// 處理鼠標(biāo)單擊事件
virtual ChangesType HandleClick(Node n, double inX, double inY,
int kstate, idvc::MouseButton Button);
// 處理工具提示事件
virtual ChangesType HandleOnTooltip(Node n, CGraphTooltipEvent* pEvent); }; // 結(jié)束 INodeHandler
}; // 結(jié)束 INodeHandler
class ILayout
{
public:
/// 這個(gè)函數(shù)應(yīng)為給定節(jié)點(diǎn)內(nèi)的所有節(jié)點(diǎn)
/// 生成新布局,并計(jì)算新布局的尺寸。內(nèi)部節(jié)點(diǎn)的位置
/// 必須根據(jù)給定節(jié)點(diǎn)的左上角進(jìn)行定義,
/// 假使給定節(jié)點(diǎn)的坐標(biāo)是 (0,0)。
virtual void Make( Node ) = 0;
/// 與 Make 一樣,但是應(yīng)使用以前布局
/// 的信息,然后嘗試保持已擺放節(jié)點(diǎn)
/// 的相對(duì)位置。
virtual void Update( Node ) = 0;
/// 這個(gè)函數(shù)在更改了已擁有節(jié)點(diǎn)的尺寸
/// 或跳過(guò) ILayout 的參數(shù)時(shí)
/// 用于重新計(jì)算節(jié)點(diǎn)與邊的坐標(biāo)。
/// 它假定以前調(diào)用過(guò) Make 或 Update,而且
/// 所有已擁有節(jié)點(diǎn)的尺寸。與 Make 和 Update 不同,它
/// 不可遞歸。
virtual void Resize( Node ) = 0;
};
以上類(lèi)定義了三種不同情況下的布局策略函數(shù),即:
定義這種區(qū)別的主要目的是為了減少布局計(jì)算時(shí)間。如果向某個(gè)大型圖表中添加一個(gè)節(jié)點(diǎn),就不需要重新計(jì)算整個(gè)圖表的布局。
快速繪制和事件處理
應(yīng)解決的最后但并非不重要的問(wèn)題是,如何快速對(duì)事件做出反應(yīng)(至少是重新繪制事件)。當(dāng)處理大型數(shù)據(jù)集時(shí),控件應(yīng)允許您快速地滾動(dòng)和縮放內(nèi)容。此處的主要問(wèn)題是,事件處理和繪圖函數(shù)是由用戶定義的(通過(guò)上面描述的界面),而控件中的每個(gè)元素在繪圖和事件處理中可以采用自己的實(shí)現(xiàn)方式。因此,不能保證快速進(jìn)行處理。不過(guò),可以減少元素函數(shù)調(diào)用的數(shù)量。
void CContent::DrawContent(idvc::IPainter* p)
{
// 確定應(yīng)重新繪制的無(wú)效長(zhǎng)方形
idvcfrw::CInvalidRegion InvalidRegions(draw_rect, valid_rect);
for(int i = 0; i < InvalidRegions.size(); ++i)
{
// 得到對(duì)應(yīng)于下一個(gè)無(wú)效區(qū)域的長(zhǎng)方形
idvc::drect rect = InvalidRegions[i];
// 查找并重新繪制與無(wú)效長(zhǎng)方形相交的節(jié)點(diǎn)
NodeSet nodes = graph->HitNodeTest(rect.left, rect.top, rect.right, rect.bottom);
for_each(nodes.begin(), nodes.end(), DrawNode(p,scale));
// 查找并重新繪制與無(wú)效長(zhǎng)方形相交的邊
EdgeSet es = graph->HitEdgeTest(rect.left, rect.top, rect.right, rect.bottom);
for_each(es->Begin(), es->End(), DrawEdge(p,scale));
};
};
利用窗口事件中也擁有發(fā)生事件的點(diǎn)或長(zhǎng)方形這一事實(shí),可采用與繪圖類(lèi)似的方式來(lái)組織事件處理(至少對(duì)于窗口事件而言)。這樣,控件可以確定節(jié)點(diǎn)與邊,這些節(jié)點(diǎn)與邊受任何給定事件的影響,而且只針對(duì)這些元素調(diào)用事件處理函數(shù),因此大大地減少了處理時(shí)間。
數(shù)據(jù)加載
當(dāng)處理大型數(shù)據(jù)集時(shí),這些數(shù)據(jù)集通常存儲(chǔ)在某個(gè)外部數(shù)據(jù)源中。外部數(shù)據(jù)源可能隨著應(yīng)用程序的不同而有所不同(文件、數(shù)據(jù)庫(kù)等)。因此,您需要使用一種機(jī)制來(lái)獨(dú)立地從外部數(shù)據(jù)源快速地加載數(shù)據(jù)。快速常常意味著加載部分?jǐn)?shù)據(jù),因?yàn)槿绻嬲孬@得大型數(shù)據(jù)集,無(wú)論如何都不能快速地執(zhí)行加載。但是,控件一般只需要數(shù)據(jù)中的一小部分來(lái)進(jìn)行處理,您應(yīng)當(dāng)只加載這一部分?jǐn)?shù)據(jù)。
有兩種方法可用來(lái)實(shí)現(xiàn)部分加載。第一種類(lèi)似于上面說(shuō)明的快速繪圖和事件處理。您需要定義一個(gè)界面,這個(gè)界面與數(shù)據(jù)源無(wú)關(guān),可用來(lái)加載數(shù)據(jù)。您應(yīng)當(dāng)嘗試實(shí)現(xiàn)以下方法,即可以用于定義需要加載的數(shù)據(jù),而不是執(zhí)行全部加載。然后可以定義應(yīng)加載的數(shù)據(jù)元素,通過(guò)界面只針對(duì)這些元素調(diào)用加載函數(shù)。
不總是可以將元素定義為自動(dòng)加載。另一種實(shí)現(xiàn)部分加載的方法是讓用戶輸入。這種情況下,用戶負(fù)責(zé)定義應(yīng)何時(shí)加載數(shù)據(jù),以及應(yīng)加載哪些數(shù)據(jù)。
ChangesType PortNodeHandler::HandleClick(Node n, double inX, double inY,
int kstate, idvc::MouseButton Button)
{
ChangesType processed = ctNone;
idvc::dpoint pos = n->GetPosition();
idvc::dsize size = n->GetSize();
// 如果節(jié)點(diǎn)沒(méi)有嵌套的節(jié)點(diǎn)且使用鼠標(biāo)左鍵對(duì)其單擊
if ( (n->GetOwned()->GetCount() == 0) && (Button == idvc::mbLeft) )
{
if (node_drawer.IsLeftPortClicked(n, inX, inY))
{
bool hide = ( CountAllParents(n) == CountVisibleParents(n) );
// 如果所有父節(jié)點(diǎn)可見(jiàn)
if( hide ) Fold(n, fdParents);
else Unfold(n, fdParents);
}
else if (node_drawer.IsRightPortClicked(n, inX, inY))
{
bool hide = ( CountAllChildren(n) == CountVisibleChildren(n) );
// 如果所有子節(jié)點(diǎn)可見(jiàn)
if( hide ) Fold(n, fdChildren);
else Unfold(n, fdChildren);
}
else
{
// 如果用戶單擊節(jié)點(diǎn)本身,則會(huì)選中它
SetFlag(n, Node::fSelected, !IsFlagSet(n, Node::fSelected));
};
processed = ctAll;
};
OnClick.fire(n, inX, inY, kstate, Button);
return processed;
};
結(jié)論
以下是創(chuàng)建可移植、快速控件中的主要概念:
下圖描述了如何將這些原則應(yīng)用到圖表控件:
圖 1. 圖表設(shè)計(jì)
使用這些原則可獲得高度自定義、可移植且快速的控件,這些控件可處理大型數(shù)據(jù)集,也可進(jìn)行調(diào)整以便用于很多應(yīng)用程序。
本站文章除注明轉(zhuǎn)載外,均為本站原創(chuàng)或翻譯。歡迎任何形式的轉(zhuǎn)載,但請(qǐng)務(wù)必注明出處、不得修改原文相關(guān)鏈接,如果存在內(nèi)容上的異議請(qǐng)郵件反饋至chenjj@fc6vip.cn
文章轉(zhuǎn)載自:網(wǎng)絡(luò)轉(zhuǎn)載