조성현

add combobox

## Ignore Visual Studio temporary files, build results, and
## files generated by popular Visual Studio add-ons.
##
## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
# User-specific files
*.suo
*.user
*.userosscache
*.sln.docstates
# User-specific files (MonoDevelop/Xamarin Studio)
*.userprefs
# Build results
[Dd]ebug/
[Dd]ebugPublic/
[Rr]elease/
[Rr]eleases/
x64/
x86/
bld/
[Bb]in/
[Oo]bj/
[Ll]og/
# Visual Studio 2015 cache/options directory
.vs/
# Uncomment if you have tasks that create the project's static files in wwwroot
#wwwroot/
# MSTest test Results
[Tt]est[Rr]esult*/
[Bb]uild[Ll]og.*
# NUNIT
*.VisualState.xml
TestResult.xml
# Build Results of an ATL Project
[Dd]ebugPS/
[Rr]eleasePS/
dlldata.c
# .NET Core
project.lock.json
project.fragment.lock.json
artifacts/
**/Properties/launchSettings.json
*_i.c
*_p.c
*_i.h
*.ilk
*.meta
*.obj
*.pch
*.pdb
*.pgc
*.pgd
*.rsp
*.sbr
*.tlb
*.tli
*.tlh
*.tmp
*.tmp_proj
*.log
*.vspscc
*.vssscc
.builds
*.pidb
*.svclog
*.scc
# Chutzpah Test files
_Chutzpah*
# Visual C++ cache files
ipch/
*.aps
*.ncb
*.opendb
*.opensdf
*.sdf
*.cachefile
*.VC.db
*.VC.VC.opendb
# Visual Studio profiler
*.psess
*.vsp
*.vspx
*.sap
# TFS 2012 Local Workspace
$tf/
# Guidance Automation Toolkit
*.gpState
# ReSharper is a .NET coding add-in
_ReSharper*/
*.[Rr]e[Ss]harper
*.DotSettings.user
# JustCode is a .NET coding add-in
.JustCode
# TeamCity is a build add-in
_TeamCity*
# DotCover is a Code Coverage Tool
*.dotCover
# Visual Studio code coverage results
*.coverage
*.coveragexml
# NCrunch
_NCrunch_*
.*crunch*.local.xml
nCrunchTemp_*
# MightyMoose
*.mm.*
AutoTest.Net/
# Web workbench (sass)
.sass-cache/
# Installshield output folder
[Ee]xpress/
# DocProject is a documentation generator add-in
DocProject/buildhelp/
DocProject/Help/*.HxT
DocProject/Help/*.HxC
DocProject/Help/*.hhc
DocProject/Help/*.hhk
DocProject/Help/*.hhp
DocProject/Help/Html2
DocProject/Help/html
# Click-Once directory
publish/
# Publish Web Output
*.[Pp]ublish.xml
*.azurePubxml
# TODO: Comment the next line if you want to checkin your web deploy settings
# but database connection strings (with potential passwords) will be unencrypted
*.pubxml
*.publishproj
# Microsoft Azure Web App publish settings. Comment the next line if you want to
# checkin your Azure Web App publish settings, but sensitive information contained
# in these scripts will be unencrypted
PublishScripts/
# NuGet Packages
*.nupkg
# The packages folder can be ignored because of Package Restore
**/packages/*
# except build/, which is used as an MSBuild target.
!**/packages/build/
# Uncomment if necessary however generally it will be regenerated when needed
#!**/packages/repositories.config
# NuGet v3's project.json files produces more ignorable files
*.nuget.props
*.nuget.targets
# Microsoft Azure Build Output
csx/
*.build.csdef
# Microsoft Azure Emulator
ecf/
rcf/
# Windows Store app package directories and files
AppPackages/
BundleArtifacts/
Package.StoreAssociation.xml
_pkginfo.txt
# Visual Studio cache files
# files ending in .cache can be ignored
*.[Cc]ache
# but keep track of directories ending in .cache
!*.[Cc]ache/
# Others
ClientBin/
~$*
*~
*.dbmdl
*.dbproj.schemaview
*.jfm
*.pfx
*.publishsettings
orleans.codegen.cs
# Since there are multiple workflows, uncomment next line to ignore bower_components
# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
#bower_components/
# RIA/Silverlight projects
Generated_Code/
# Backup & report files from converting an old project file
# to a newer Visual Studio version. Backup files are not needed,
# because we have git ;-)
_UpgradeReport_Files/
Backup*/
UpgradeLog*.XML
UpgradeLog*.htm
# SQL Server files
*.mdf
*.ldf
*.ndf
# Business Intelligence projects
*.rdl.data
*.bim.layout
*.bim_*.settings
# Microsoft Fakes
FakesAssemblies/
# GhostDoc plugin setting file
*.GhostDoc.xml
# Node.js Tools for Visual Studio
.ntvs_analysis.dat
node_modules/
# Typescript v1 declaration files
typings/
# Visual Studio 6 build log
*.plg
# Visual Studio 6 workspace options file
*.opt
# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
*.vbw
# Visual Studio LightSwitch build output
**/*.HTMLClient/GeneratedArtifacts
**/*.DesktopClient/GeneratedArtifacts
**/*.DesktopClient/ModelManifest.xml
**/*.Server/GeneratedArtifacts
**/*.Server/ModelManifest.xml
_Pvt_Extensions
# Paket dependency manager
.paket/paket.exe
paket-files/
# FAKE - F# Make
.fake/
# JetBrains Rider
.idea/
*.sln.iml
# CodeRush
.cr/
# Python Tools for Visual Studio (PTVS)
__pycache__/
*.pyc
# Cake - Uncomment if you are using it
# tools/**
# !tools/packages.config
# Telerik's JustMock configuration file
*.jmconfig
# BizTalk build output
*.btp.cs
*.btm.cs
*.odx.cs
*.xsd.cs
# C++ objects and libs
*.slo
*.lo
*.o
*.a
*.la
*.lai
*.so
*.dll
*.dylib
# Qt-es
/.qmake.cache
/.qmake.stash
*.pro.user
*.pro.user.*
*.qbs.user
*.qbs.user.*
*.moc
moc_*.cpp
qrc_*.cpp
ui_*.h
Makefile*
*build-*
# QtCreator
*.autosave
# QtCtreator Qml
*.qmlproject.user
*.qmlproject.user.*
# QtCtreator CMake
CMakeLists.txt.user*
\ No newline at end of file
#include "EdgeItem.h"
#include <QtWidgets>
void EdgeItem::mousePressEvent(QGraphicsSceneMouseEvent * event)
{
}
void EdgeItem::mouseMoveEvent(QGraphicsSceneMouseEvent * event)
{
}
void EdgeItem::mouseReleaseEvent(QGraphicsSceneMouseEvent * event)
{
}
EdgeItem::EdgeItem(double x1, double y1, double x2, double y2, QColor color, int width)
{
this->x1 = x1;
this->y1 = y1;
this->x2 = x2;
this->y2 = y2;
this->color = color;
this->width = width;
setZValue(0); //노드 앞 가리지 않도록 ZValue 설정
/*setFlags(ItemIsSelectable | ItemIsMovable);
setAcceptHoverEvents(true);*/
}
QRectF EdgeItem::boundingRect() const
{
return QRectF(0,0,0,0);
}
QPainterPath EdgeItem::shape() const
{
QPainterPath path;
return path;
}
void EdgeItem::paint(QPainter * painter, const QStyleOptionGraphicsItem * option, QWidget * widget)
{
Q_UNUSED(widget);
QPen oldPen = painter->pen();
QPen pen = oldPen;
pen.setWidth(width);
pen.setColor(color);
painter->setPen(pen);
painter->drawLine(QLineF(x1, y1, x2, y2));
}
#ifndef EDGEITEM_H
#define EDGEITEM_H
#include <QColor>
#include <QGraphicsItem>
class EdgeItem
: public QGraphicsItem
{
private:
double x1, y1, x2, y2;
int width;
QColor color;
protected:
void mousePressEvent(QGraphicsSceneMouseEvent *event) override;
void mouseMoveEvent(QGraphicsSceneMouseEvent *event) override;
void mouseReleaseEvent(QGraphicsSceneMouseEvent *event) override;
public:
EdgeItem(double x1, double y1, double x2, double y2, QColor color, int width);
QRectF boundingRect() const override;
QPainterPath shape() const override;
void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) override;
};
#endif // EDGEITEM_H
\ No newline at end of file
#include "GraphItem.h"
#include <boost/graph/fruchterman_reingold.hpp>
#include <boost/graph/random_layout.hpp>
#include <boost/graph/circle_layout.hpp>
#include <boost/graph/dijkstra_shortest_paths.hpp>
#include <boost/algorithm/string.hpp> //boost::split
#include <boost/bimap.hpp>
#include <boost/regex.hpp>
#include <exception>
#include <iterator>
#include <string>
#include <map>
#include <vector>
#include <QDebug>
#include <QtWidgets>
GraphItem::GraphItem(ifstream& fin)
{
if (!fin)
throw std::exception("graph file input is invalid");
/**
* Parse Paper dataset
* - paper_key, [author_list], publish_year
* Column Delimiter: ||
* Author list Delimiter: &&
*/
std::string line;
vector<std::string> tokens;
vector<std::string> authors;
vector<pair<string, string>> edges;
//String <--> int 양방향 변환을 위해 bidirectional map 상숑
//map<string, int> -> <vertex label, vertex index>
typedef boost::bimap<string, int> bm_type;
bm_type node_ids;
vector<simple_edge> edges_indexes; //int로 변환된 edge
int node_cnt = 0;
qDebug() << "* graph reading start";
//한 줄씩 읽어서 Parse
while (std::getline(fin, line) && !line.empty()) {
//boost::split 이용해 문자열 분리
//tokens[0]: Paper-key. ex) conf/iastedCSN/KeimS06
//tokens[1]: Authors. ex) Werner Keim&&Arpad L. Scholtz
//tokens[2]: Published year.
boost::split(tokens, line, boost::is_any_of("||"), boost::token_compress_on);
boost::split(authors, tokens[1], boost::is_any_of("&&"), boost::token_compress_on);
const string& paper_key = tokens[0];
if (node_ids.left.find(paper_key) == node_ids.left.end()) {
node_ids.insert(bm_type::value_type(paper_key, node_cnt++));
}
for (auto author : authors) {
edges.push_back(pair<string, string>(paper_key, author));
if (node_ids.left.find(author) == node_ids.left.end()) {
node_ids.insert(bm_type::value_type(author, node_cnt++));
}
}
//debug
if (node_cnt > NODE_LIMIT) break;
}
qDebug() << "* graph reading complete";
qDebug() << "* # of nodes: " << node_cnt;
qDebug() << "* # of edges: " << edges.size();
//edge conversion
//<string, string> to <int, int>
//using boost::bimap (bidirectional map)
for (auto edge : edges) {
edges_indexes.push_back({
node_ids.left.find(edge.first)->get_right(),
node_ids.left.find(edge.second)->get_right()
});
}
//Graph --> defined in "PaperGraphWidget.h"
//Graph graph(edges_indexes.begin(), edges_indexes.end(), node_ids.size());
graph = new Graph(edges_indexes.begin(), edges_indexes.end(), node_ids.size());
//set index property
qDebug() << "* set vertex property start";
typedef typename graph_traits<Graph>::edge_iterator edge_iterator;
typedef typename graph_traits<Graph>::vertex_iterator vertex_iterator;
vertex_iterator vi, vi_end;
int i = 0;
for (boost::tie(vi, vi_end)=vertices(*graph); vi!=vi_end; ++vi) {
//Vertex Property 설정
//index: 0 ~ ...
//name : map의 value(i) 기준으로 찾은 Key
// map --> map<string, int> (boost bidirectional map)
boost::put(vertex_index, *graph, *vi, i);
boost::put(vertex_name, *graph, *vi,
node_ids.right.find(i)->get_left());
++i;
}
qDebug() << "* set vertex property end";
//qDebug("* set edges weight start");
////모든 edge의 weight를 1로 설정
//typename graph_traits<Graph>::edge_iterator ei, ei_end;
//for (boost::tie(ei, ei_end)=boost::edges(*graph); ei!=ei_end; ++ei) {
// boost::put(edge_weight, *graph, *ei, 1);
//}
//qDebug("* set edges weight end");
//
//
//qDebug("* path highlighting start");
////find start, end node's id
//int start_idx, end_idx;
//for (boost::tie(vi, vi_end)=vertices(*graph); vi!=vi_end; ++vi) {
// string node_name = boost::get(vertex_name, *graph, *vi);
// if (node_name == "Seong Chul Cho") {
// start_idx = boost::get(vertex_index, *graph, *vi);
// } else if (node_name == "Hyung Jin Kim") {
// end_idx = boost::get(vertex_index, *graph, *vi);
// }
//}
//typedef boost::graph_traits<Graph>::vertex_descriptor vertex_descriptor;
//vector<vertex_descriptor> parents(num_vertices(*graph));
//vector<int> distances(num_vertices(*graph));
//vertex_descriptor start_vertex = vertex(start_idx, *graph);
//dijkstra_shortest_paths(*graph, start_vertex,
// predecessor_map(&parents[0]).distance_map(&distances[0]));
////path finding
//vertex_descriptor current = boost::vertex(end_idx, *graph);
//while (current != boost::vertex(start_idx, *graph)) {
//}
//qDebug("* path highlighting end");
//graph layout calculation
//using boost::random_graph_layout and boost::kamada_kawai_spring_layout
//vertex마다 계산된 좌표를 property에 적용
//예제 코드: http://www.boost.org/doc/libs/1_63_0/libs/graph/test/layout_test.cpp
//(-> 콘솔 기반)
qDebug() << "* make graph layout start";
typedef square_topology<> Topology;
minstd_rand gen;
Topology topology(gen, (double)SCREEN_SIZE);
Topology::point_type origin;
origin[0] = origin[1] = (double)SCREEN_SIZE;
Topology::point_difference_type extent;
extent[0] = extent[1] = (double)SCREEN_SIZE;
rectangle_topology<> rect_top(gen,
-SCREEN_SIZE/2, -SCREEN_SIZE/2,
SCREEN_SIZE/2, SCREEN_SIZE/2);
switch (LAYOUT_MODE) {
case GRAPH_LAYOUT::RANDOM_LAYOUT:
random_graph_layout(*graph, get(vertex_position, *graph), rect_top);
break;
case GRAPH_LAYOUT::CIRCLE_LAYOUT:
circle_graph_layout(*graph, get(vertex_position, *graph), SCREEN_SIZE/2);
break;
case GRAPH_LAYOUT::FRUCHTERMAN_REINGOLD_LAYOUT:
fruchterman_reingold_force_directed_layout(*graph,
get(vertex_position, *graph),
topology,
attractive_force(square_distance_attractive_force())
.cooling(linear_cooling<double>(50))
);
break;
}
qDebug() << "* make graph layout end";
//add edges
typedef square_topology<> Topology;
typedef typename Topology::point_type Point;
auto position = get(vertex_position, *graph);
auto label = get(vertex_name, *graph);
typedef boost::graph_traits<Graph>::vertex_descriptor vertex_descriptor;
typename graph_traits<Graph>::edge_iterator ei, ei_end;
vertex_descriptor u, v;
for (boost::tie(ei, ei_end)=boost::edges(*graph); ei!=ei_end; ++ei) {
u = source(*ei, *graph);
v = target(*ei, *graph);
Point p1 = position[u];
Point p2 = position[v];
//make edge item and push it to list
EdgeItem *edge;
//if (label[u] == "conf/sbrn/GomesPSRC10" ||
// label[u] == "conf/iastedCSN/KeimS06" ||
// label[v] == "conf/sbrn/GomesPSRC10" ||
// label[v] == "conf/iastedCSN/KeimS06") {
// //highlight
// edge = new EdgeItem(p1[0], p1[1], p2[0], p2[1], QColor(Qt::blue), 3);
//} else {
// edge = new EdgeItem(p1[0], p1[1], p2[0], p2[1], QColor(Qt::black), 0);
//}
edge = new EdgeItem(p1[0], p1[1], p2[0], p2[1], QColor(Qt::black), 0);
edge->setPos(p1[0], p1[1]);
edgeList << edge;
}
//add nodes
for (boost::tie(vi, vi_end)=vertices(*graph); vi!=vi_end; ++vi) {
Point p = position[*vi];
std::string name = label[*vi];
//make node item and push it to list
NodeItem *node;
//if (name == "conf/sbrn/GomesPSRC10" ||
// name == "conf/iastedCSN/KeimS06") {
// //highlight
// node = new NodeItem(p[0], p[1], QColor(Qt::blue), QString(name.c_str()));
//} else {
// node = new NodeItem(p[0], p[1], QColor(Qt::green), QString(name.c_str()));
//}
node = new NodeItem(p[0], p[1], QColor(Qt::green), QString(name.c_str()));
node->setPos(QPointF(p[0], p[1]));
nodeList << node;
}
}
//override
QRectF GraphItem::boundingRect() const
{
//TODO
return QRectF(-SCREEN_SIZE/2, -SCREEN_SIZE/2, SCREEN_SIZE, SCREEN_SIZE);
}
QPainterPath GraphItem::shape() const
{
QPainterPath path;
return path;
}
void GraphItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
{
if (!graph)
return;
//debug
//-> x, y, w, h
//현재 Graph의 bounding rect 출력
/*QPen oldPen = painter->pen();
QPen pen = oldPen;
pen.setColor(Qt::red);
painter->setPen(pen);
painter->drawRect(QRectF(-SCREEN_SIZE/2, -SCREEN_SIZE/2, SCREEN_SIZE, SCREEN_SIZE));*/
//print edges
for (auto edge: edgeList) {
edge->paint(painter, option, widget);
}
//print nodes
for (auto node: nodeList) {
node->paint(painter, option, widget);
}
}
//event handler
void GraphItem::mousePressEvent(QGraphicsSceneMouseEvent *event)
{
}
void GraphItem::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
{
}
void GraphItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
{
}
#ifndef GRAPHITEM_H
#define GRAPHITEM_H
#include <QGraphicsItem>
#include <QList>
#include <QColor>
#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/topology.hpp>
#include <boost/graph/graph_traits.hpp>
#include <fstream>
#include "NodeItem.h"
#include "EdgeItem.h"
using namespace std;
using namespace boost;
enum GRAPH_LAYOUT {
RANDOM_LAYOUT,
CIRCLE_LAYOUT,
//KAMADA_KAWAI_LAYOUT,
FRUCHTERMAN_REINGOLD_LAYOUT //slow
};
/**
* Constants
*/
const int LAYOUT_MODE = GRAPH_LAYOUT::RANDOM_LAYOUT;
const int SCREEN_SIZE = 300;
const int NODE_LIMIT = 100;
enum vertex_position_t { vertex_position };
namespace boost {
BOOST_INSTALL_PROPERTY(vertex, position);
}
typedef square_topology<>::point_type point;
struct simple_edge {
int first, second;
};
typedef boost::property<vertex_index_t, int,
boost::property<vertex_name_t, std::string,
boost::property<vertex_position_t, point>>
> VertexProperties;
typedef adjacency_list<
listS, //outEdgeList
listS, //VertexList
undirectedS,
//vertex properties
VertexProperties,
//edge properties
boost::property<edge_weight_t, double>
> Graph;
class GraphItem
: public QGraphicsItem
{
public:
GraphItem(ifstream& fin);
//overrides
QRectF boundingRect() const override;
QPainterPath shape() const override;
void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) override;
//methods
void path_highlighting(std::string start, std::string end);
//
protected:
void mousePressEvent(QGraphicsSceneMouseEvent *event) override;
void mouseMoveEvent(QGraphicsSceneMouseEvent *event) override;
void mouseReleaseEvent(QGraphicsSceneMouseEvent *event) override;
private:
Graph *graph = nullptr;
QList<NodeItem *> nodeList;
QList<EdgeItem *> edgeList;
};
#endif // GRAPHITEM_H
#include "GraphicsView.h"
#include <qmath.h>
#include <QKeyEvent>
//View
View::View(const QString& name, QWidget *parent)
......@@ -12,8 +13,6 @@ View::View(const QString& name, QWidget *parent)
graphicsView->setViewportUpdateMode(QGraphicsView::SmartViewportUpdate);
graphicsView->setTransformationAnchor(QGraphicsView::AnchorUnderMouse);
//connect
//layout
QGridLayout *topLayout = new QGridLayout;
topLayout->addWidget(graphicsView, 0, 0);
......@@ -29,11 +28,27 @@ QGraphicsView* View::view() const
void View::setupMatrix()
{
qreal scale = qPow(qreal(2), qreal(2));
qreal scale = qPow(qreal(1), qreal(1));
QMatrix matrix;
matrix.scale(scale, scale);
matrix.rotate(qreal(0));
graphicsView->setMatrix(matrix);
}
\ No newline at end of file
}
#ifndef QT_NO_WHEELEVENT
void GraphicsView::wheelEvent(QWheelEvent * event)
{
scaleView(pow((double)2, event->delta() / 240.0));
}
#endif
void GraphicsView::scaleView(qreal scaleFactor)
{
qreal factor = transform().scale(scaleFactor, scaleFactor).mapRect(QRectF(0, 0, 1, 1)).width();
if (factor < 0.07 || factor > 100)
return;
scale(scaleFactor, scaleFactor);
}
......
......@@ -16,6 +16,12 @@ class GraphicsView
private:
View* view;
protected:
#ifndef QT_NO_WHEELEVENT
void wheelEvent(QWheelEvent *event) override;
#endif
void scaleView(qreal scaleFactor);
public:
GraphicsView(View *v): QGraphicsView(), view(v) {};
};
......
#include "NodeItem.h"
#include <QtWidgets>
#include <QMessageBox>
void NodeItem::mousePressEvent(QGraphicsSceneMouseEvent * event)
{
/*if (event->button() == Qt::LeftButton) {
QMessageBox msgbox;
msgbox.setText("hi");
msgbox.setInformativeText("hi2");
msgbox.setStandardButtons(QMessageBox::Ok);
msgbox.setDefaultButton(QMessageBox::Ok);
msgbox.exec();
}*/
}
void NodeItem::mouseMoveEvent(QGraphicsSceneMouseEvent * event)
......@@ -13,16 +23,17 @@ void NodeItem::mouseReleaseEvent(QGraphicsSceneMouseEvent * event)
{
}
NodeItem::NodeItem(int x, int y, QString label)
NodeItem::NodeItem(double x, double y, QColor color, QString label)
{
//node constructor
this->x = x;
this->y = y;
this->color = QColor(Qt::green);
this->color = color;
this->label = label;
setZValue(1);
setFlags(ItemIsSelectable | ItemIsMovable);
setAcceptHoverEvents(true);
//setFlags(ItemIsSelectable | ItemIsMovable);
//setAcceptHoverEvents(true);
}
QRectF NodeItem::boundingRect() const
......@@ -33,7 +44,6 @@ QRectF NodeItem::boundingRect() const
QPainterPath NodeItem::shape() const
{
QPainterPath path;
//path.addRect(14, 14, 82, 42);
path.addRect(0, 0, NODE_SIZE, NODE_SIZE);
return path;
}
......@@ -42,23 +52,24 @@ void NodeItem::paint(QPainter * painter, const QStyleOptionGraphicsItem * option
{
Q_UNUSED(widget);
//set node pen style
QPen oldPen = painter->pen();
QPen pen = oldPen;
pen.setWidth(0);
pen.setColor(QColor(Qt::black));
painter->setPen(pen);
//label
QFont font("Gulim", 10);
font.setStyleStrategy(QFont::ForceOutline);
QFont font("Gulim", 3); //set font, font size
painter->setFont(font);
painter->save();
painter->scale(0.3, 0.3);
painter->drawText(0, 0, label);
painter->drawText(x, y, label);
painter->restore();
//Rectangle
//node rectangle
QColor fillColor = (option->state & QStyle::State_Selected) ? color.dark(150) : color;
if (option->state & QStyle::State_MouseOver) fillColor = fillColor.light(125);
QPen pen = painter->pen();
pen.setWidth(0);
pen.setColor(QColor(Qt::black));
painter->setPen(pen);
painter->setBrush(QBrush(fillColor));
painter->drawRect(0, 0, NODE_SIZE, NODE_SIZE);
painter->drawRect(x, y, NODE_SIZE, NODE_SIZE);
}
......
......@@ -11,8 +11,8 @@ class NodeItem
: public QGraphicsItem
{
private:
int x;
int y;
double x;
double y;
QColor color;
QString label;
......@@ -22,7 +22,7 @@ protected:
void mouseReleaseEvent(QGraphicsSceneMouseEvent *event) override;
public:
NodeItem(int x, int y, QString label);
NodeItem(double x, double y, QColor color, QString label);
QRectF boundingRect() const override;
QPainterPath shape() const override;
......
......@@ -145,6 +145,7 @@
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="EdgeItem.cpp" />
<ClCompile Include="GeneratedFiles\Debug\moc_GraphicsView.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
......@@ -172,6 +173,7 @@
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="GraphicsView.cpp" />
<ClCompile Include="GraphItem.cpp" />
<ClCompile Include="main.cpp" />
<ClCompile Include="NodeItem.cpp" />
<ClCompile Include="PaperGraphWidget.cpp" />
......@@ -217,6 +219,7 @@
</CustomBuild>
</ItemGroup>
<ItemGroup>
<ClInclude Include="EdgeItem.h" />
<ClInclude Include="GeneratedFiles\ui_PaperGraphWidget.h" />
<CustomBuild Include="GraphicsView.h">
<AdditionalInputs Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">$(QTDIR)\bin\moc.exe;%(FullPath)</AdditionalInputs>
......@@ -236,6 +239,7 @@
<Outputs Condition="'$(Configuration)|$(Platform)'=='Release|x64'">.\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp</Outputs>
<Command Condition="'$(Configuration)|$(Platform)'=='Release|x64'">"$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" -DUNICODE -DWIN32 -DQT_DLL -DQT_NO_DEBUG -DNDEBUG -DQT_CORE_LIB -DQT_GUI_LIB -DQT_WIDGETS_LIB "-I.\GeneratedFiles" "-I." "-I$(QTDIR)\include" "-I.\GeneratedFiles\$(ConfigurationName)\." "-I$(QTDIR)\include\QtCore" "-I$(QTDIR)\include\QtGui" "-I$(QTDIR)\include\QtWidgets"</Command>
</CustomBuild>
<ClInclude Include="GraphItem.h" />
<ClInclude Include="NodeItem.h" />
</ItemGroup>
<ItemGroup>
......
......@@ -62,6 +62,12 @@
<ClCompile Include="GeneratedFiles\Release\moc_GraphicsView.cpp">
<Filter>Generated Files\Release</Filter>
</ClCompile>
<ClCompile Include="GraphItem.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="EdgeItem.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<CustomBuild Include="PaperGraphWidget.h">
......@@ -84,5 +90,11 @@
<ClInclude Include="NodeItem.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="GraphItem.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="EdgeItem.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
</Project>
\ No newline at end of file
......
......@@ -3,6 +3,9 @@
#include "GraphicsView.h"
#include <string>
#include <QComboBox>
#include <QMessageBox>
#include <QtGui>
PaperGraphWidget::PaperGraphWidget(QWidget *parent)
: QWidget(parent)
......@@ -12,77 +15,40 @@ PaperGraphWidget::PaperGraphWidget(QWidget *parent)
View *view = new View("temp view");
view->view()->setScene(scene);
QHBoxLayout *layout = new QHBoxLayout;
QVBoxLayout *layout = new QVBoxLayout;
QComboBox *combo = new QComboBox;
combo->addItem("conf/iastedCSN/KeimS06");
combo->addItem("conf/iastedCSN/Mojumdar06");
combo->addItem("conf/iastedCSN/PourKKI06");
connect(combo, SIGNAL(currentIndexChanged(int)),
this, SLOT(handleSelectionChanged(int)));
layout->addWidget(combo);
layout->addWidget(view);
setLayout(layout);
setWindowTitle(tr("dblp paper graph visualization"));
}
void PaperGraphWidget::print_graph(const Graph & graph)
void PaperGraphWidget::print_graph(ifstream& fin)
{
//print graph
typedef square_topology<> Topology;
typedef typename Topology::point_type Point;
const int rect_sz = 4;
auto position = get(vertex_position, graph);
auto label = get(vertex_name, graph);
//print edges
typename graph_traits<Graph>::edge_iterator ei, ei_end;
typedef boost::graph_traits<Graph>::vertex_descriptor VertexDescriptor;
VertexDescriptor u, v;
for (boost::tie(ei, ei_end)=edges(graph); ei!=ei_end; ++ei) {
u = source(*ei, graph);
v = target(*ei, graph);
Point p1 = position[u];
Point p2 = position[v];
/*line = scene->addLine(
p1[0], p1[1],
p2[0], p2[1],
QPen(Qt::black)
);*/
//QGraphicsItem *edge =
// new EdgeItem(p1[0], p1[1], p2[0], p2[1]);
//edge->setPos(QPointF(p1[0], p1[1]));
//scene->addItem(edge);
scene->addLine(p1[0], p1[1], p2[0], p2[1], QPen(Qt::black, 0));
}
QGraphicsItem *graph_item = new GraphItem(fin);
graph_item->setPos(0, 0);
scene->addItem(graph_item);
}
//print nodes
typename graph_traits<Graph>::vertex_iterator vi, vi_end;
for (boost::tie(vi, vi_end)=vertices(graph); vi!=vi_end; ++vi) {
//Point p = position[*vi];
//rectangle = scene->addRect(
// p[0], p[1], rect_sz, rect_sz,
// QPen(Qt::black),
// QBrush(Qt::green));
Point p = position[*vi];
std::string name = label[*vi];
QGraphicsItem *node = new NodeItem(p[0], p[1], QString(name.c_str()));
node->setPos(QPointF(p[0], p[1]));
scene->addItem(node);
void PaperGraphWidget::handleSelectionChanged(int idx)
{
/*QMessageBox::information(this, "QCombobox",
"idx: "+QString::number(idx));*/
if (idx==0) {
} else if (idx==1) {
} else {
}
}
void PaperGraphWidget::initscene()
{
scene = new QGraphicsScene(this);
//int x = 0, y;
//for (int i=-11000; i<11000; i+=110) {
// ++x;
// y = 0;
// for (int j=-7000; j<7000; j+=70) {
// ++y;
// QGraphicsItem *item = new NodeItem(x, y);
// item->setPos(QPointF(i, j));
// scene->addItem(item);
// }
//}
}
......
......@@ -4,46 +4,22 @@
#include <QtWidgets/QWidget>
#include <QGraphicsScene>
#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/topology.hpp>
#include <boost/graph/graph_traits.hpp>
#include <fstream>
#include "GraphItem.h"
#include "ui_PaperGraphWidget.h"
using namespace boost;
enum vertex_position_t { vertex_position };
namespace boost {
BOOST_INSTALL_PROPERTY(vertex, position);
}
typedef square_topology<>::point_type point;
struct simple_edge {
int first, second;
};
typedef boost::property<vertex_index_t, int,
boost::property<vertex_name_t, std::string,
boost::property<vertex_position_t, point>>
> VertexProperties;
typedef adjacency_list<
listS, //outEdgeList
listS, //VertexList
undirectedS,
//vertex properties
VertexProperties,
//edge properties
boost::property<edge_weight_t, double>
> Graph;
class PaperGraphWidget : public QWidget
{
Q_OBJECT
public:
PaperGraphWidget(QWidget *parent = 0);
void print_graph(const Graph& graph);
void print_graph(ifstream& fin);
private slots:
void handleSelectionChanged(int idx);
private:
void initscene();
......
......@@ -14,7 +14,6 @@
//#include <boost/graph/kamada_kawai_spring_layout.hpp>
#include <boost/graph/random_layout.hpp>
#include <boost/graph/circle_layout.hpp>
#include <boost/graph/dijkstra_shortest_paths.hpp>
#include <boost/algorithm/string.hpp> //boost::split
#include <boost/bimap.hpp>
......@@ -24,187 +23,22 @@ using namespace std;
* Constants
*/
const char* PAPER_FILENAME = "dblp-paper.txt";
enum GRAPH_LAYOUT {
RANDOM_LAYOUT,
CIRCLE_LAYOUT,
//KAMADA_KAWAI_LAYOUT,
FRUCHTERMAN_REINGOLD_LAYOUT //slow
};
const int LAYOUT_MODE = GRAPH_LAYOUT::RANDOM_LAYOUT;
const int SCREEN_SIZE = 500;
const int NODE_LIMIT = 100;
Graph read_graph(ifstream& in) throw(std::exception) {
/**
* Parse Paper dataset
* - paper_key, [author_list], publish_year
* Column Delimiter: ||
* Author list Delimiter: &&
*/
std::string line;
vector<std::string> tokens;
vector<std::string> authors;
vector<pair<string, string>> edges;
//String <--> int 양방향 변환을 위해 bidirectional map 상숑
//map<string, int> -> <vertex label, vertex index>
typedef boost::bimap<string, int> bm_type;
bm_type node_ids;
vector<simple_edge> edges_indexes; //int로 변환된 edge
int node_cnt = 0;
qDebug() << "* graph reading start";
//한 줄씩 읽어서 Parse
while (std::getline(in, line) && !line.empty()) {
//boost::split 이용해 문자열 분리
//tokens[0]: Paper-key. ex) conf/iastedCSN/KeimS06
//tokens[1]: Authors. ex) Werner Keim&&Arpad L. Scholtz
//tokens[2]: Published year.
boost::split(tokens, line, boost::is_any_of("||"), boost::token_compress_on);
boost::split(authors, tokens[1], boost::is_any_of("&&"), boost::token_compress_on);
const string& paper_key = tokens[0];
if (node_ids.left.find(paper_key) == node_ids.left.end()) {
node_ids.insert(bm_type::value_type(paper_key, node_cnt++));
}
for (auto author : authors) {
edges.push_back(pair<string, string>(paper_key, author));
if (node_ids.left.find(author) == node_ids.left.end()) {
node_ids.insert(bm_type::value_type(author, node_cnt++));
}
}
//debug
if (node_cnt > NODE_LIMIT) break;
}
qDebug() << "* graph reading complete";
qDebug() << "* # of nodes: " << node_cnt;
qDebug() << "* # of edges: " << edges.size();
//edge conversion
//<string, string> to <int, int>
//using boost::bimap (bidirectional map)
for (auto edge : edges) {
edges_indexes.push_back({
node_ids.left.find(edge.first)->get_right(),
node_ids.left.find(edge.second)->get_right()
});
}
//Graph --> defined in "PaperGraphWidget.h"
Graph graph(edges_indexes.begin(), edges_indexes.end(), node_ids.size());
//set index property
qDebug() << "* set vertex property start";
typedef typename graph_traits<Graph>::edge_iterator edge_iterator;
typedef typename graph_traits<Graph>::vertex_iterator vertex_iterator;
vertex_iterator vi, vi_end;
int i = 0;
for (boost::tie(vi, vi_end)=vertices(graph); vi!=vi_end; ++vi) {
//Vertex Property 설정
//index: 0 ~ ...
//name : map의 value(i) 기준으로 찾은 Key
// map --> map<string, int> (boost bidirectional map)
boost::put(vertex_index, graph, *vi, i);
boost::put(vertex_name, graph, *vi,
node_ids.right.find(i)->get_left());
++i;
}
qDebug() << "* set vertex property end";
//모든 edge weight 1로 만들기
//edge_iterator ei, ei_end;
//for (boost::tie(ei, ei_end)=boost::edges(graph); ei!=ei_end; ++ei) {
// boost::put(edge_weight, graph, *ei, 1);
//}
////path finding between two vertices
////using dijkstra algorithm
////ex) "Werner Keim" ---> "Arpad L. Scholtz" (in dataset 2nd line)
//qDebug() << "* path finding start";
//typedef graph_traits<Graph>::vertex_descriptor vertex_descriptor;
//std::vector<vertex_descriptor> parent(num_vertices(graph));
//std::vector<int> distance(num_vertices(graph));
//int start_idx = node_ids.left.find("Werner Keim")->get_right();
//vertex_descriptor start_v = vertex(start_idx, graph);
//dijkstra_shortest_paths(
// graph,
// start_v,
// predecessor_map(
// boost::make_iterator_property_map(parent.begin(), get(boost::vertex_index, graph))
// ).distance_map(boost::make_iterator_property_map(distance.begin(), get(boost::vertex_index, graph)))
//);
//qDebug() << "* path finding end";
//graph layout calculation
//using boost::random_graph_layout and boost::kamada_kawai_spring_layout
//vertex마다 계산된 좌표를 property에 적용
//예제 코드: http://www.boost.org/doc/libs/1_63_0/libs/graph/test/layout_test.cpp
//(-> 콘솔 기반)
qDebug() << "* make graph layout start";
typedef square_topology<> Topology;
minstd_rand gen;
Topology topology(gen, (double)SCREEN_SIZE);
Topology::point_type origin;
origin[0] = origin[1] = (double)SCREEN_SIZE;
Topology::point_difference_type extent;
extent[0] = extent[1] = (double)SCREEN_SIZE;
rectangle_topology<> rect_top(gen,
-SCREEN_SIZE/2, -SCREEN_SIZE/2,
SCREEN_SIZE/2, SCREEN_SIZE/2);
switch (LAYOUT_MODE) {
case GRAPH_LAYOUT::RANDOM_LAYOUT:
random_graph_layout(graph, get(vertex_position, graph), rect_top);
break;
case GRAPH_LAYOUT::CIRCLE_LAYOUT:
circle_graph_layout(graph, get(vertex_position, graph), SCREEN_SIZE/2);
break;
//case GRAPH_LAYOUT::KAMADA_KAWAI_LAYOUT:
// kamada_kawai_spring_layout(graph,
// get(vertex_position, graph),
// get(edge_weight, graph),
// topology,
// side_length((double)SCREEN_SIZE)
// );
// break;
case GRAPH_LAYOUT::FRUCHTERMAN_REINGOLD_LAYOUT:
fruchterman_reingold_force_directed_layout(graph,
get(vertex_position, graph),
topology,
attractive_force(square_distance_attractive_force())
.cooling(linear_cooling<double>(50))
);
break;
}
qDebug() << "* make graph layout end";
return graph;
}
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
//app.setAttribute(Qt::AA_DontCreateNativeWidgetSiblings);
PaperGraphWidget w;
try {
ifstream fin(PAPER_FILENAME);
w.print_graph(read_graph(fin));
w.print_graph(fin);
fin.close();
} catch (const std::exception& e) {
qDebug() << "Error: " << e.what();
return EXIT_FAILURE;
}
w.show();
return app.exec();
......