在程序内部使用winGraphviz进行图形自动布局
winGraphviz支持內部圖形形狀進行佈局圖輸出。當然,在我們程序內部並不需要這樣的一個圖,因為我們的圖可能需要其它的繪製元素,而且我們還會在圖形上進行拖動、放大、縮小等功能,一張簡單的佈局圖是不符合我們的要求的。
幸好winGraphviz支持佈局格局的輸出,牠可以告訴我們每一個具體的圖形的位置,每一條連線的位置,只要我們獲取到該位置,即可以用該位置進行設置我們內部的圖形的位置了,這樣,我們就可以達到我們的目的了。
對於圖形坐標的輸出,winGraphviz提供有兩個接口,一個是ToPlain,另一個是ToSvg。我在研究StarUML時,發現牠是使用ToPlain的,但我在程序內部使用ToPlain時,發現坐標出入甚大,不明其原理如何,且因StarUML使用DELPHI編寫,代碼也稍顯複雜,而且基於時間問題,我并未深入研究,總之ToPlain並不能產生確切的坐標,可能還需要其它處理。不過ToSvg則提供了正確的坐標位置,使用它將可以得到我們想要的坐標,確切的坐標。牠是一個XML格式,讀者可以使用一個簡單的圖形,如Graphic g {a -> b -> c; } 來查看輸出的Svg格式是如何樣子,當然接下來我會告訴讀者如何使用COM進行加載winGraphviz,可以先看看我用程序自己畫的佈局圖:
左邊是winGraphviz生成的,右邊是我自己畫的。
加載COM接口:
USES_CONVERSION;
IDOT* m_pDot
HRESULT hr; hr = CoInitialize(NULL);
if (FAILED (hr))
{
ASSERT(0);
return false;
} hr = CoCreateInstance(CLSID_DOT, NULL, CLSCTX_ALL, IID_IDOT, reinterpret_cast<LPVOID*>(&m_pDot)); if (FAILED (hr))
{
return false;
} return true;
將用戶自定義的節點、連線轉換成winGraphviz語言(假設使用是一個vector的節點和一個vector的連線):
CMyGraphvizGraph* CDiagramLayout::LayoutDiagram( IGraphicvizView* pView, bool fLeftToRight /*= true*/ )
{
if (m_pDot == NULL || pView == NULL)
{
return NULL;
} m_pView = pView; string strGraphviz ("digraph G { ordering=out;");
if (fLeftToRight)
{
strGraphviz += " rankdir=LR;";
}
else
{
strGraphviz += " rankdir=TB;";
} const TPNodeList& vctNodes = pView->GetNodes();
for (size_t i = ; i < vctNodes.size(); ++i)
{
strGraphviz += GetNodeString (vctNodes [i]);
} const TPEdgeList& vctEdges= pView->GetEdges();
for (size_t i = ; i < vctEdges.size(); ++i)
{
strGraphviz += GetEdgeString (vctEdges [i]);
} strGraphviz += "}"; return GenerateGraph(strGraphviz.c_str());
} CMyGraphvizGraph* CDiagramLayout::GenerateGraph( const char* pszToParse)
{
BSTR bsSvg = NULL;
HRESULT hr = m_pDot->ToSvg(A2BSTR(pszToParse), &bsSvg); //BSTR bsPlain = NULL;
//m_pDot->ToPlain(A2BSTR(pszToParse), &bsPlain); #ifdef _DEBUG
IBinaryImage* image = NULL;
hr = m_pDot->ToPNG(A2BSTR(pszToParse), &image); if (FAILED (hr))
{
ASSERT ();
return NULL;
} VARIANT_BOOL fRet = FALSE;
hr = image->Save(L"d:\\a.png", &fRet); image->Release(); if (FAILED (hr) || !fRet)
{
ASSERT(); return false;
}
#endif int nLen = SysStringLen(bsSvg); //not including NULL
int nBytes = WideCharToMultiByte(CP_ACP, , bsSvg, nLen,
NULL, NULL, NULL, NULL); //number of bytes not including NULL
BSTR bstrA = SysAllocStringByteLen(NULL, nBytes); // allocates nBytes
VERIFY(WideCharToMultiByte(CP_ACP, , bsSvg, nLen, (LPSTR)bstrA, nBytes, NULL,
NULL) == nBytes); CMyGraphvizGraph* pGraph = m_parser.Parse((char*)bstrA);
::SysFreeString(bsSvg);
::SysFreeString(bstrA); return pGraph;
} string CDiagramLayout::GetEdgeString( GraphvizEdge* pEdge )
{
static char szRet []; sprintf (szRet, "%s -> %s [label=%s];",
pEdge->strHead.c_str(),
pEdge->strTail.c_str(),
pEdge->strName.c_str()); return szRet;
} string CDiagramLayout::GetNodeString( GraphvizNode* pNode )
{
static char szRet []; sprintf (szRet, "%s [shape=box,width=%4.3f,height=%4.3f];",
pNode->strName.c_str(),
(float)pNode->iWidth / PIXEL_PER_INCH_WIDTH (),
(float)pNode->iHeight / PIXEL_PER_INCH_HEIGHT ()); return szRet;
}
關於解析svg的代碼:
CMyGraphvizGraph* CGraphvizPlainOutputParser::ParseSvgText( const char* pszText )
{
TiXmlDocument doc;
doc.Parse(pszText);
if (doc.Error())
{
return NULL;
} CMyGraphvizGraph* pGraph = new CMyGraphvizGraph; TiXmlElement* pRoot = doc.RootElement(); const char* pszWidth = pRoot->Attribute("width");
const char* pszHeight = pRoot->Attribute("height"); if (pszWidth != NULL && pszHeight != NULL)
{
pGraph->SetWidth(atoi (pszWidth));
pGraph->SetHeight(atoi (pszHeight)); for (TiXmlElement* pItem = pRoot->FirstChildElement ()->FirstChildElement(); pItem != NULL; pItem = pItem->NextSiblingElement())
{
const char* pszClass = pItem->Attribute("class");
if (pszClass == NULL)
{
continue;
} if (stricmp (pszClass, "node") == )
{
TiXmlElement* pTitle = pItem->FirstChildElement("text");
TiXmlElement* pPolygon = pItem->FirstChildElement("polygon"); if (pTitle != NULL && pPolygon != NULL)
{
const char* pszName = pTitle->GetText();
const char* pszPts = pPolygon->Attribute("points"); if (pszName == NULL || pszPts == NULL)
{
assert ();
continue;
} vector <string> vctPoints = split(pszPts, ", "); GraphvizNode* pNode = new GraphvizNode; pNode->strName = pszName; int iTop = ;
int iBottom = ;
int iLeft = ;
int iRight = ; int iX, iY;
for (size_t i = ; i < vctPoints.size(); i += )
{
iX = atoi (vctPoints [i].c_str());
iY = atoi (vctPoints [i + ].c_str());
if (iX < iLeft) iLeft = iX;
if (iY < iTop) iTop = iY;
if (iX > iRight) iRight = iX;
if (iY > iBottom) iBottom = iY;
}
pNode->iLeft = iLeft;
pNode->iTop = iTop;
pNode->iWidth = iRight - iLeft;
pNode->iHeight = iBottom - iTop; pGraph->AddNode(pNode);
}
}
else if (stricmp (pszClass, "edge") == )
{
TiXmlElement* pTitle = pItem->FirstChildElement("text");
TiXmlElement* pPath = pItem->FirstChildElement("path");
TiXmlElement* pPolygon = pItem->FirstChildElement("polygon"); if (pTitle != NULL && pPath != NULL && pPolygon != NULL)
{
const char* pszName = pTitle->GetText();
const char* pszPts = pPolygon->Attribute("points");
const char* pszPath = pPath->Attribute("d"); if (pszName == NULL || pszPts == NULL || pszPath == NULL)
{
assert ();
continue;
} vector <string> vctPathPoints = split(pszPath, "MC, ");
vector <string> vctPolygonPoints = split(pszPts, ", "); GraphvizEdge* pEdge = new GraphvizEdge;
pEdge->strName = pszName; for (size_t i = ; i < vctPathPoints.size(); i += )
{
POINT pt = {atoi (vctPathPoints [i].c_str()), atoi (vctPathPoints [i + ].c_str())};
pEdge->ptsLine.push_back(pt);
} for (size_t j = ; j < vctPolygonPoints.size(); j += )
{
POINT pt = {atoi (vctPolygonPoints [j].c_str()), atoi (vctPolygonPoints [j + ].c_str())};
pEdge->ptsArrow.push_back(pt);
} pGraph->AddEdge(pEdge);
}
}
}
} return pGraph;
}
解析svg
最後顯示:
void CGraphDlg::OnPaint()
{
CPaintDC dc(this); // device context for painting
// TODO: Add your message handler code here
// Do not call CDialog::OnPaint() for painting messages
if (m_pGraph != NULL)
{
CDC dcMem;
CBitmap bmp; dcMem.CreateCompatibleDC(&dc);
bmp.CreateCompatibleBitmap(&dc, m_pGraph->GetWidth(), m_pGraph->GetHeight());
dcMem.SelectObject(&bmp); const TPNodeList& vctNodes = m_pGraph->GetNodes();
const TPEdgeList& vctEdges = m_pGraph->GetEdges(); for (size_t i = ; i < vctNodes.size(); ++i)
{
RECT rc = {
vctNodes [i]->iLeft,
vctNodes [i]->iTop,
vctNodes [i]->iWidth + vctNodes [i]->iLeft,
vctNodes [i]->iHeight + vctNodes [i]->iTop
}; dcMem.FillSolidRect(&rc, RGB (, , )); CString str (vctNodes [i]->strName.c_str());
dcMem.DrawText (str, &rc, DT_VCENTER | DT_SINGLELINE | DT_CENTER);
} CPen pen (PS_SOLID, , RGB (, , ));
CBrush br (RGB (, , ));
dcMem.SelectObject(&pen);
dcMem.SelectObject(&br); for (size_t j = ; j < vctEdges.size(); ++j)
{
size_t iLineSize = vctEdges [j]->ptsLine.size();
POINT* pptLine = new POINT [iLineSize]; for (size_t k = ; k < iLineSize; ++k)
{
pptLine [k] = vctEdges [j]->ptsLine [k];
}
dcMem.PolyBezier(pptLine, iLineSize); // 箭头
size_t iArrowSize = vctEdges [j]->ptsArrow.size();
POINT* pptsArrow = new POINT [iArrowSize];
for (size_t m = ; m < iArrowSize; ++m)
{
pptsArrow [m] = vctEdges [j]->ptsArrow [m];
} dcMem.Polygon(pptsArrow, iArrowSize);
delete [] pptsArrow;
delete [] pptLine;
} dc.BitBlt(, , m_pGraph->GetWidth(), m_pGraph->GetHeight(), &dcMem, , , SRCCOPY); bmp.DeleteObject();
dcMem.DeleteDC();
}
}
顯示圖
完整的代碼:
#pragma once #include "NodeEdge.h"
#include "WinGraphviz/WinGraphviz.h"
#include "IGraphicvizView.h" #include <vector>
#include <string>
using std::vector;
using std::string; class CGraphvizPlainOutputParser; class CMyGraphvizGraph
{
friend class CGraphvizPlainOutputParser;
private:
int m_iWidth;
int m_iHeight; TPNodeList m_nodes;
TPEdgeList m_edges; public:
CMyGraphvizGraph ();
~CMyGraphvizGraph(); GraphvizNode* GetNode (size_t iIndex);
size_t GetNodeCount () const;
GraphvizEdge* GetEdge (size_t iIndex);
size_t GetEdgeCount () const; const TPNodeList& GetNodes () { return m_nodes; };
const TPEdgeList& GetEdges () { return m_edges; } int GetWidth () { return m_iWidth; };
int GetHeight () { return m_iHeight; }; protected:
void SetWidth (int iWidth);
void SetHeight (int iHeight);
void AddNode( GraphvizNode* pNode );
void AddEdge( GraphvizEdge* pEdge );
}; class CGraphvizPlainOutputParser
{
private:
vector <string> m_vctTempStr; private:
double InchToPixel (double dblInch, BOOL fWidth);
double InchStrToPixel (const char* pszInch, BOOL fWidth); void ParseLine (const char* pszLine, vector <string>& vctOut);
CMyGraphvizGraph* ParseGraphicLine (const char* pszLine);
GraphvizNode* ParseNodeLine (const char* pszLine);
GraphvizEdge* ParseEdgeLine (const char* pszLine); void SeparateLine( const char* pszText, std::vector<string>& vctPlainOut ); public:
CGraphvizPlainOutputParser ();
~CGraphvizPlainOutputParser(); CMyGraphvizGraph* Parse (const char* pszText);
CMyGraphvizGraph* ParsePlainText( const char* pszText );
CMyGraphvizGraph* ParseSvgText( const char* pszText );
}; class CDiagramLayout
{
private:
IDOT* m_pDot; // viz图
IGraphicvizView* m_pView; // 显示图
CGraphvizPlainOutputParser m_parser; // 解析器,用于分析viz布局后的图 bool Init(); string GetNodeIdString ();
string GetEdgeIdString (); CMyGraphvizGraph* GenerateGraph (const char* pszToParse);
string GetEdgeString( GraphvizEdge* pEdge );
string GetNodeString( GraphvizNode* pNode ); public:
CDiagramLayout ();
~CDiagramLayout(); CMyGraphvizGraph* LayoutDiagram (IGraphicvizView* pView, bool fLeftToRight = true);
};
MyGraphvizGraph.h
#include "stdafx.h"
#include "MyGraphvizGraph.h"
#include "tinyxml/tinyxml.h"
#include <shlwapi.h>
#include <math.h> #pragma comment (lib, "shlwapi.lib")
#pragma warning (disable : 4996) int PIXEL_PER_INCH_WIDTH ()
{
HWND hwnd = GetDesktopWindow();
HDC hdc = GetDC(hwnd);
int iPixel = GetDeviceCaps (hdc, LOGPIXELSX);
ReleaseDC(hwnd, hdc); return iPixel;
} int PIXEL_PER_INCH_HEIGHT ()
{
HWND hwnd = GetDesktopWindow();
HDC hdc = GetDC(hwnd);
int iPixel = GetDeviceCaps (hdc, LOGPIXELSY);
ReleaseDC(hwnd, hdc); return iPixel;
} //字符串分割函数
static std::vector<std::string> split(const char* pszToSplit, const char* pszSpplitor)
{
std::vector<std::string> result;
char* pszTemp = strdup (pszToSplit); char* token = strtok(pszTemp , pszSpplitor );
while( token != NULL )
{
/* While there are tokens in "string" */
result.push_back(token); /* Get next token: */
token = strtok( NULL, pszSpplitor );
} free (pszTemp);
return result;
} GraphvizNode* CMyGraphvizGraph::GetNode( size_t iIndex )
{
if (iIndex >= m_nodes.size())
{
return NULL;
} return m_nodes [iIndex];
} size_t CMyGraphvizGraph::GetNodeCount() const
{
return m_nodes.size();
} GraphvizEdge* CMyGraphvizGraph::GetEdge( size_t iIndex )
{
if (iIndex >= m_edges.size())
{
return NULL;
} return m_edges [iIndex];
} size_t CMyGraphvizGraph::GetEdgeCount() const
{
return m_edges.size();
} CMyGraphvizGraph::CMyGraphvizGraph()
: m_iWidth ()
, m_iHeight ()
{ } CMyGraphvizGraph::~CMyGraphvizGraph()
{
for (size_t i = ; i < m_nodes.size(); ++i)
{
delete m_nodes [i];
}
m_nodes.clear(); for (size_t j = ; j < m_edges.size(); ++j)
{
delete m_edges [j];
}
m_edges.clear();
} void CMyGraphvizGraph::SetWidth( int iWidth )
{
m_iWidth = iWidth;
} void CMyGraphvizGraph::SetHeight( int iHeight )
{
m_iHeight = iHeight;
} void CMyGraphvizGraph::AddNode( GraphvizNode* pNode )
{
m_nodes.push_back(pNode);
} void CMyGraphvizGraph::AddEdge( GraphvizEdge* pEdge )
{
m_edges.push_back(pEdge);
} double CGraphvizPlainOutputParser::InchToPixel( double dblInch, BOOL fWidth )
{
return dblInch * (fWidth ? PIXEL_PER_INCH_WIDTH () : PIXEL_PER_INCH_HEIGHT ());
} double CGraphvizPlainOutputParser::InchStrToPixel( const char* pszInch, BOOL fWidth )
{
char* pszEnd = NULL;
return InchToPixel (strtod (pszInch, &pszEnd), fWidth);
} void CGraphvizPlainOutputParser::ParseLine( const char* pszLine, vector <string>& vctOut )
{
char chSeparator = ' ';
const char* pszNext = NULL; vctOut.clear();
char* pszNewString = strdup (pszLine);
StrTrimA (pszNewString, " "); const char* pszTemp = pszNewString;
do
{
pszNext = strchr (pszTemp, chSeparator);
if (pszNext != NULL)
{
vctOut.push_back(string (pszTemp, , pszNext - pszTemp));
}
else
{
vctOut.push_back(pszTemp);
break;
} pszTemp = pszNext + ; /* ' ' 的下一个字符位置 */ while (*pszTemp == ' ' && *pszTemp != '\0')
{
++pszTemp;
} } while (pszNext != NULL); free (pszNewString);
} CMyGraphvizGraph* CGraphvizPlainOutputParser::ParseGraphicLine( const char* pszLine )
{
CMyGraphvizGraph* pGraph = NULL; m_vctTempStr.clear();
ParseLine(pszLine, m_vctTempStr); ASSERT(m_vctTempStr.size() >= ); pGraph = new CMyGraphvizGraph;
pGraph->SetWidth((int)InchStrToPixel(m_vctTempStr [].c_str(), TRUE));
pGraph->SetHeight((int)InchStrToPixel(m_vctTempStr [].c_str(), FALSE)); return pGraph;
} GraphvizNode* CGraphvizPlainOutputParser::ParseNodeLine( const char* pszLine )
{
GraphvizNode* pNode = NULL; m_vctTempStr.clear();
ParseLine(pszLine, m_vctTempStr); pNode = new GraphvizNode;
pNode->strName = m_vctTempStr [];
pNode->iLeft = (int)InchStrToPixel (m_vctTempStr [].c_str(), TRUE);
pNode->iTop = (int)InchStrToPixel (m_vctTempStr [].c_str(), FALSE);
pNode->iWidth = (int)InchStrToPixel (m_vctTempStr [].c_str(), TRUE);
pNode->iHeight = (int)InchStrToPixel (m_vctTempStr [].c_str(), FALSE); return pNode;
} GraphvizEdge* CGraphvizPlainOutputParser::ParseEdgeLine( const char* pszLine )
{
GraphvizEdge* pEdge = NULL;
int iCount, i, iX, iY; m_vctTempStr.clear();
ParseLine(pszLine, m_vctTempStr); pEdge = new GraphvizEdge;
pEdge->strHead = m_vctTempStr [];
pEdge->strTail = m_vctTempStr [];
iCount = atoi (m_vctTempStr [].c_str ()); pEdge->ptsLine.clear(); for (i = ; i < iCount; ++i)
{
iX = (int)InchStrToPixel(m_vctTempStr [ + i * ].c_str (), TRUE);
iY = (int)InchStrToPixel(m_vctTempStr [ + i * + ].c_str(), FALSE); POINT pt = {iX, iY};
pEdge->ptsLine.push_back(pt);
} pEdge->strName = m_vctTempStr [ + iCount * ]; return pEdge;
} CGraphvizPlainOutputParser::CGraphvizPlainOutputParser()
{ } CGraphvizPlainOutputParser::~CGraphvizPlainOutputParser()
{ } CMyGraphvizGraph* CGraphvizPlainOutputParser::Parse( const char* pszText )
{
#ifdef PARSE_PLAIN
return ParsePlainText (pszText);
#else
return ParseSvgText (pszText);
#endif
} void CGraphvizPlainOutputParser::SeparateLine( const char* pszText, std::vector<string>& vctPlainOut )
{
const char* pszTemp = pszText; while (pszTemp != NULL && *pszTemp != '\0')
{
const char* pszNext = strchr (pszTemp, '\n'); if (pszNext == NULL)
{
break;
} vctPlainOut.push_back(string (pszTemp, , pszNext - pszTemp));
pszTemp = pszNext + ;
}
} CMyGraphvizGraph* CGraphvizPlainOutputParser::ParsePlainText( const char* pszText )
{
static const char* TOKEN4_NODE = "node";
static const char* TOKEN4_EDGE = "edge"; std::vector <string> vctPlainOut; CMyGraphvizGraph* pGraph = NULL;
GraphvizNode* pNode = NULL;
GraphvizEdge* pEdge = NULL; string strToken; size_t i; // 将多行文本分解成VECTOR
SeparateLine (pszText, vctPlainOut);
pGraph = ParseGraphicLine(vctPlainOut [].c_str()); // 解析字符串
for (i = ; i < vctPlainOut.size(); ++i)
{
if (strnicmp (vctPlainOut [i].c_str(), TOKEN4_NODE, ) == )
{
pGraph->AddNode (ParseNodeLine (vctPlainOut [i].c_str()));
}
else if (strnicmp(vctPlainOut [i].c_str(), TOKEN4_EDGE, ) == )
{
pGraph->AddEdge (ParseEdgeLine (vctPlainOut [i].c_str()));
}
} return pGraph;
} CMyGraphvizGraph* CGraphvizPlainOutputParser::ParseSvgText( const char* pszText )
{
TiXmlDocument doc;
doc.Parse(pszText);
if (doc.Error())
{
return NULL;
} CMyGraphvizGraph* pGraph = new CMyGraphvizGraph; TiXmlElement* pRoot = doc.RootElement(); const char* pszWidth = pRoot->Attribute("width");
const char* pszHeight = pRoot->Attribute("height"); if (pszWidth != NULL && pszHeight != NULL)
{
pGraph->SetWidth(atoi (pszWidth));
pGraph->SetHeight(atoi (pszHeight)); for (TiXmlElement* pItem = pRoot->FirstChildElement ()->FirstChildElement(); pItem != NULL; pItem = pItem->NextSiblingElement())
{
const char* pszClass = pItem->Attribute("class");
if (pszClass == NULL)
{
continue;
} if (stricmp (pszClass, "node") == )
{
TiXmlElement* pTitle = pItem->FirstChildElement("text");
TiXmlElement* pPolygon = pItem->FirstChildElement("polygon"); if (pTitle != NULL && pPolygon != NULL)
{
const char* pszName = pTitle->GetText();
const char* pszPts = pPolygon->Attribute("points"); if (pszName == NULL || pszPts == NULL)
{
assert ();
continue;
} vector <string> vctPoints = split(pszPts, ", "); GraphvizNode* pNode = new GraphvizNode; pNode->strName = pszName; int iTop = ;
int iBottom = ;
int iLeft = ;
int iRight = ; int iX, iY;
for (size_t i = ; i < vctPoints.size(); i += )
{
iX = atoi (vctPoints [i].c_str());
iY = atoi (vctPoints [i + ].c_str());
if (iX < iLeft) iLeft = iX;
if (iY < iTop) iTop = iY;
if (iX > iRight) iRight = iX;
if (iY > iBottom) iBottom = iY;
}
pNode->iLeft = iLeft;
pNode->iTop = iTop;
pNode->iWidth = iRight - iLeft;
pNode->iHeight = iBottom - iTop; pGraph->AddNode(pNode);
}
}
else if (stricmp (pszClass, "edge") == )
{
TiXmlElement* pTitle = pItem->FirstChildElement("text");
TiXmlElement* pPath = pItem->FirstChildElement("path");
TiXmlElement* pPolygon = pItem->FirstChildElement("polygon"); if (pTitle != NULL && pPath != NULL && pPolygon != NULL)
{
const char* pszName = pTitle->GetText();
const char* pszPts = pPolygon->Attribute("points");
const char* pszPath = pPath->Attribute("d"); if (pszName == NULL || pszPts == NULL || pszPath == NULL)
{
assert ();
continue;
} vector <string> vctPathPoints = split(pszPath, "MC, ");
vector <string> vctPolygonPoints = split(pszPts, ", "); GraphvizEdge* pEdge = new GraphvizEdge;
pEdge->strName = pszName; for (size_t i = ; i < vctPathPoints.size(); i += )
{
POINT pt = {atoi (vctPathPoints [i].c_str()), atoi (vctPathPoints [i + ].c_str())};
pEdge->ptsLine.push_back(pt);
} for (size_t j = ; j < vctPolygonPoints.size(); j += )
{
POINT pt = {atoi (vctPolygonPoints [j].c_str()), atoi (vctPolygonPoints [j + ].c_str())};
pEdge->ptsArrow.push_back(pt);
} pGraph->AddEdge(pEdge);
}
}
}
} return pGraph;
} CDiagramLayout::CDiagramLayout()
{
Init ();
} CDiagramLayout::~CDiagramLayout()
{
if (m_pDot != NULL)
{
m_pDot->Release();
m_pDot = NULL;
} CoUninitialize();
} bool CDiagramLayout::Init()
{
m_pView = NULL;
m_pDot = NULL; USES_CONVERSION;
HRESULT hr;
CComBSTR result; hr = CoInitialize(NULL);
if (FAILED (hr))
{
ASSERT();
return false;
} hr = CoCreateInstance(CLSID_DOT, NULL, CLSCTX_ALL, IID_IDOT, reinterpret_cast<LPVOID*>(&m_pDot)); if (FAILED (hr))
{
ASSERT();
return false;
} return true;
} CMyGraphvizGraph* CDiagramLayout::LayoutDiagram( IGraphicvizView* pView, bool fLeftToRight /*= true*/ )
{
if (m_pDot == NULL || pView == NULL)
{
return NULL;
} m_pView = pView; string strGraphviz ("digraph G { ordering=out;");
if (fLeftToRight)
{
strGraphviz += " rankdir=LR;";
}
else
{
strGraphviz += " rankdir=TB;";
} const TPNodeList& vctNodes = pView->GetNodes();
for (size_t i = ; i < vctNodes.size(); ++i)
{
strGraphviz += GetNodeString (vctNodes [i]);
} const TPEdgeList& vctEdges= pView->GetEdges();
for (size_t i = ; i < vctEdges.size(); ++i)
{
strGraphviz += GetEdgeString (vctEdges [i]);
} strGraphviz += "}"; return GenerateGraph(strGraphviz.c_str());
} CMyGraphvizGraph* CDiagramLayout::GenerateGraph( const char* pszToParse)
{
BSTR bsSvg = NULL;
HRESULT hr = m_pDot->ToSvg(A2BSTR(pszToParse), &bsSvg); //BSTR bsPlain = NULL;
//m_pDot->ToPlain(A2BSTR(pszToParse), &bsPlain); #ifdef _DEBUG
IBinaryImage* image = NULL;
hr = m_pDot->ToPNG(A2BSTR(pszToParse), &image); if (FAILED (hr))
{
ASSERT ();
return NULL;
} VARIANT_BOOL fRet = FALSE;
hr = image->Save(L"d:\\a.png", &fRet); image->Release(); if (FAILED (hr) || !fRet)
{
ASSERT(); return false;
}
#endif int nLen = SysStringLen(bsSvg); //not including NULL
int nBytes = WideCharToMultiByte(CP_ACP, , bsSvg, nLen,
NULL, NULL, NULL, NULL); //number of bytes not including NULL
BSTR bstrA = SysAllocStringByteLen(NULL, nBytes); // allocates nBytes
VERIFY(WideCharToMultiByte(CP_ACP, , bsSvg, nLen, (LPSTR)bstrA, nBytes, NULL,
NULL) == nBytes); CMyGraphvizGraph* pGraph = m_parser.Parse((char*)bstrA);
::SysFreeString(bsSvg);
::SysFreeString(bstrA); return pGraph;
} string CDiagramLayout::GetEdgeString( GraphvizEdge* pEdge )
{
static char szRet []; sprintf (szRet, "%s -> %s [label=%s];",
pEdge->strHead.c_str(),
pEdge->strTail.c_str(),
pEdge->strName.c_str()); return szRet;
} string CDiagramLayout::GetNodeString( GraphvizNode* pNode )
{
static char szRet []; sprintf (szRet, "%s [shape=box,width=%4.3f,height=%4.3f];",
pNode->strName.c_str(),
(float)pNode->iWidth / PIXEL_PER_INCH_WIDTH (),
(float)pNode->iHeight / PIXEL_PER_INCH_HEIGHT ()); return szRet;
}
MyGraphvizGraph.cpp
#pragma once #include <vector>
#include <string> using std::vector;
using std::string; struct GraphvizNode
{
string strName;
int iLeft;
int iTop;
int iWidth;
int iHeight;
}; typedef vector <GraphvizNode*> TPNodeList; struct GraphvizEdge
{
string strName;
string strHead; // head node
string strTail; // tail node vector <POINT> ptsLine;
vector <POINT> ptsArrow; // used for svg
}; typedef vector <GraphvizEdge*> TPEdgeList;
NodeEdge.h
#pragma once #include "NodeEdge.h" #include <vector>
using std::vector; class IGraphicvizView
{
public:
virtual const TPNodeList& GetNodes () = ;
virtual const TPEdgeList& GetEdges () = ;
};
IGraphicvizView.h
使用:
CDiagramLayout m_layout;
CMyGraphvizGraph* m_pGraph; InitGraphic (); m_pGraph = m_layout.LayoutDiagram(this, m_fLeftToRight); // -----------------------------------
void CGraphDlg::InitGraphic()
{
for (int i = ; i < ; ++i)
{
GraphvizNode* pNode = new GraphvizNode;
char szName [];
sprintf (szName, "n%d", i); pNode->strName = szName;
pNode->iLeft = ;
pNode->iTop = ;
pNode->iWidth = ;
pNode->iHeight = ; m_vctNodes.push_back(pNode);
} GraphvizEdge* pEdge1 = new GraphvizEdge;
pEdge1->strHead = "n1";
pEdge1->strTail = "n0";
pEdge1->strName = "e0";
m_vctEdges.push_back(pEdge1); GraphvizEdge* pEdge2 = new GraphvizEdge;
pEdge2->strHead = "n2";
pEdge2->strTail = "n0";
pEdge2->strName = "e1";
m_vctEdges.push_back(pEdge2); GraphvizEdge* pEdge3 = new GraphvizEdge;
pEdge3->strHead = "n3";
pEdge3->strTail = "n0";
pEdge3->strName = "e2";
m_vctEdges.push_back(pEdge3); GraphvizEdge* pEdge4 = new GraphvizEdge;
pEdge4->strHead = "n4";
pEdge4->strTail = "n3";
pEdge4->strName = "e3";
m_vctEdges.push_back(pEdge4); GraphvizEdge* pEdge5 = new GraphvizEdge;
pEdge5->strHead = "n1";
pEdge5->strTail = "n2";
pEdge5->strName = "e4";
m_vctEdges.push_back(pEdge5); //GraphvizEdge* pEdge6 = new GraphvizEdge;
//pEdge6->strHead = "n0";
//pEdge6->strTail = "n3";
//pEdge6->strName = "e5";
//m_vctEdges.push_back(pEdge6); }
需要注意的是:
1)winGraphviz語言使用的單位是英吋,所以要獲取屏幕像素與英吋的比例。
2)如果文字長度超過圖形長度,有可能會將圖形擴寬,以便顯示完整的文字。
畢。
在程序内部使用winGraphviz进行图形自动布局的更多相关文章
- windows程序内部运行机制
Windows程序内部运行机制 2007-10-21 19:52 1010人阅读 评论(0) 收藏 举报 windowsvc++applicationcallbackwinapistructure W ...
- 转:"在已损坏了程序内部状态的XXX.exe 中发生了缓冲区溢出"的一种可能原因
我的问题跟原作者的问题差不多.头文件和DLL不匹配导致的. 原文链接:http://blog.csdn.net/u012494876/article/details/39030887 今天软件突然出现 ...
- [置顶] iOS 应用程序内部国际化,不跟随系统语言
前言:网络上关于iOS国际化的文章很多,但基本上都是基于跟随系统语言的国际化,笔者就不赘述了-0 – 今天要讲的是不跟随系统的切换语言版本方案,即程序内部的切换语言版本方案. 一.总则: 应用内部语言 ...
- Android应用程序内部启动Activity过程(startActivity)的源代码分析
文章转载至CSDN社区罗升阳的安卓之旅,原文地址:http://blog.csdn.net/luoshengyang/article/details/6703247 上文介绍了Android应用程序的 ...
- 小程序 web-view 嵌套的网页跳转到小程序内部页面 实现无缝连接
需要在H5页面被作出判断和处理 点击事件发生时跳转到小程序内部页面 1.引入小程序提供的JS <script type="text/javascript" src=&quo ...
- uwsgi+django架构程序内部无法获取全局变量
近期开发了一个djangoi程序,用django自带的python manage.py runserver 0.0.0.0:80 运行方式无任何问题,但用django+nginx+uwsg部署运行有时 ...
- Java编写画图板程序细节-保存已画图形
没有Java编写画图板程序细节-保存已画图形 一.为何我们要保存画图板上已画图形呢? 有很多人会问,为什么我们一定要保存画图板上已经画好了的图形呢?原因很简单.当我们在画图板上画完自己想画的图形后 ...
- iOS 应用程序内部国际化,不跟随系统语言
前言:网络上关于iOS国际化的文章很多,但基本上都是基于跟随系统语言的国际化,笔者就不赘述了-0 – 今天要讲的是不跟随系统的切换语言版本方案,即程序内部的切换语言版本方案. 一.总则: 应用内部语言 ...
- Windows程序内部运行机制 转自http://www.cnblogs.com/zhili/p/WinMain.html
一.引言 要想熟练掌握Windows应用程序的开发,首先需要理解Windows平台下程序运行的内部机制,然而在.NET平台下,创建一个Windows桌面程序,只需要简单地选择Windows窗体应用程序 ...
随机推荐
- C# 解决窗体假死的状态
异步调用是CLR为开发者提供的一种重要的编程手段,它也是构建高性能.可伸缩应用程序的关键.在多核CPU越来越普及的今天,异步编程允许使用非常少的线程执行很多操作.我们通常使用异步完成许多计算型.IO型 ...
- ITFriend站点内測公測感悟
4月份做出站点Demo.就開始让用户使用了. 最初的黄色版界面.被吐槽得比較厉害. 关于界面.每一个人都有自己的看法,仅仅是喜欢和不喜欢的人比例不一样. 后来.花3400元请了个设计师,设计了一套界面 ...
- Mycat探索之旅(5)----常用的分片规则
分片枚举 通过在配置文件中配置可能的枚举id,自己配置分片,本规则适用于特定的场景,比如有些业务需要按照省份或区县来做保存, 而全国省份区县固定的,这类业务使用本条规则,配置如下: <table ...
- hibernate学习系列-----(7)hibernate对集合属性的操作之List集合篇
今天要写的内容其实不多,本打算将hibernate对集合的操作的内容直接归结为一篇的,但想一想,还是分开写的比较好,毕竟前面的已经发布出去来了,废话不多说,开始吧! 依旧新建一个StudentList ...
- django迁移model到别的app中
举例: 移动 users.AccessKey 到 authentication.AccessKey中 1. 移动models到新的app中 $ mv users/models/access_key.p ...
- 用聚合数据API(苏州实时公交API)快速写出小程序
利用聚合数据API快速写出小程序,过程简单. 1.申请小程序账号 2.进入开发 3.调用API.比如“苏州实时公交”小程序,选择的是苏州实时公交API. 苏州实时公交API文档:https://www ...
- 9、Linux驱动的杂项设备
杂项设备,是字符设备中的特殊,它的主设备号,是 10,不同的杂项设备,通过次设备号进行区分. 1.注册与注销 int misc_register(struct miscdevice * misc) 完 ...
- 从头认识Spring-3.8 简单的AOP日志实现(注解版)-扩展添加检查订单功能,以便记录并检測输入的參数
这一章节我们讨论一下扩展添加检查订单功能,以便记录并检測输入的參数. 1.domain 蛋糕类: package com.raylee.my_new_spring.my_new_spring.ch03 ...
- PHP excel读取excel文件转换为数组
/*备注:先去下载PHP EXCEL——http://download-codeplex.sec.s-msft.com/Download/Release?ProjectName=phpexcel&am ...
- java - day15 - nstInner
匿名内部类 package com.javatest.mama; public class Mama { int x = 5; public static void main(String[] arg ...