https://github.com/ialhashim/topo-blend
Revision 39b13612ebd645a65eda854771b517371f2f858a authored by ennetws on 13 March 2015, 18:17:18 UTC, committed by ennetws on 13 March 2015, 18:17:18 UTC
1 parent c702819
Raw File
Tip revision: 39b13612ebd645a65eda854771b517371f2f858a authored by ennetws on 13 March 2015, 18:17:18 UTC
Create README.md
Tip revision: 39b1361
GraphExplorer.cpp
#include <QtConcurrentRun>
#include <QSet>
#include <QStack>
#include <QFileDialog>

#include "GraphExplorer.h"
#include "ui_GraphExplorer.h"

#include "StructureGraph.h"
#include "Scheduler.h"
#include "Task.h"

#include "ExportDynamicGraph.h"

GraphExplorer::GraphExplorer(QWidget *parent): QWidget(parent), ui(new Ui::GraphExplorer), p(NULL), svgViewer(NULL)
{
	//setAttribute(Qt::WA_DeleteOnClose);
	ui->setupUi(this);

	g = NULL;
	clear();

	this->connect(ui->nodesFilterProperty, SIGNAL(textChanged(QString)), SLOT(filterNodes()));
	this->connect(ui->nodesFilterValue, SIGNAL(textChanged(QString)), SLOT(filterNodes()));

	this->connect(ui->edgesFilterProperty, SIGNAL(textChanged(QString)), SLOT(filterEdges()));
	this->connect(ui->edgesFilterValue, SIGNAL(textChanged(QString)), SLOT(filterEdges()));

	ui->nodesTree->sortByColumn(0);
	ui->edgesTree->sortByColumn(0);

	// Tree view properties
	QStringList headers;
	headers << "Name" << "Value";
	ui->nodesTree->setHeaderLabels(headers);
	ui->edgesTree->setHeaderLabels(headers);
	ui->graphTree->setHeaderLabels(headers);
}

void GraphExplorer::hideEvent(QHideEvent * e)
{
	clear();
	g = NULL;

	QWidget::hideEvent(e);
}

void GraphExplorer::update(Structure::Graph * graph)
{
	storeOldValues();
	clear();

	g = ui->isActual->isChecked() ? Structure::Graph::actualGraph(graph) : graph;

	// Set graph name
	QString graphName = g->name().split("/").last();
	ui->graphName->setText( graphName );

	// Graph visualization
	drawGraph();

	// Fill in node and edges info
	fillNodesInfo();
	fillEdgesInfo();
	fillGraphInfo();

	// Apply filters
	filterNodes();
	filterEdges();

	if(!this->isVisible()) this->show();
}

void GraphExplorer::storeOldValues()
{
	oldValues.clear();
	if(!g) return;

	clear();
	fillNodesInfo();
	fillEdgesInfo();

	// Store old values
	QTreeWidgetItemIterator nit(ui->nodesTree), eit(ui->edgesTree);
	
	while(*nit) {
		oldValues[fullName(*nit).join("")] = (*nit)->text(1);
		nit++;
	}

	while(*eit) {
		oldValues[fullName(*eit).join("")] = (*eit)->text(1);
		eit++;
	}
}

void GraphExplorer::drawGraph()
{
	if(!dotPath.size()){
		#ifdef Q_OS_WIN
		dotPath = "\"" + QString(exec("where dot").c_str()).replace("\\","/").trimmed() + "\"";
		#else
		dotPath = QString(exec("which dot").c_str());
		#endif

		if(!dotPath.size())
		{
			// Ask user for help
			dotPath = QFileDialog::getOpenFileName(0, "Graphviz dot application", "dot", tr("Application (*.*)"));
			if(dotPath == "") dotPath = "-";
		}
	}
	if(dotPath == "-") return;

	p = new QProcess;
	this->connect(p, SIGNAL(finished(int, QProcess::ExitStatus)), SLOT(drawGraphSVG()));
	p->start( dotPath, QStringList() << "-Tsvg" );
	if (!p->waitForStarted()){
		qDebug() << "Error process: " << p->error();
		return;
	}

	p->write( qPrintable(toGraphvizFormat(g,"","")) );
	p->closeWriteChannel();
	p->waitForFinished(-1);
}

void GraphExplorer::drawGraphSVG()
{
	QString svgData = p->readAllStandardOutput();
	QTemporaryFile file;
	if (file.open()){
		QTextStream out(&file);
		out << svgData;
		file.close();

		if(!svgViewer) 
		{
			svgViewer = new SvgView;
			ui->graphVizLayout->addWidget(svgViewer);
			ui->graphImage->setText("");
		}

		svgViewer->openFile(file);
	}

	p->deleteLater();
}

void GraphExplorer::fillNodesInfo()
{
	foreach(Structure::Node * n, g->nodes){
		QTreeWidgetItem * nitem = new QTreeWidgetItem;
		nitem->setText(0, n->id);
		nitem->setText(1, n->type());

		if(ui->isGeometry->isChecked()){
			QStringList geometryString;
			foreach(Vector3 p, n->controlPoints())
				geometryString << QString("%1,%2,%3").arg(p[0]).arg(p[1]).arg(p[2]);
			n->property["geometry"].setValue( QString("[%1] =>").arg(geometryString.size()) + geometryString.join(" / ") );
		}

		fillInfoItem(n->property, nitem);
		ui->nodesTree->addTopLevelItem(nitem);
	}

	// Color changes when values do
	QTreeWidgetItemIterator nit(ui->nodesTree);
	while(*nit) {
		QString key = fullName(*nit).join("");
		if(oldValues.contains(key)){
			if(oldValues[key] != (*nit)->text(1)){
				(*nit)->setForeground( 0, QBrush(Qt::red) );
				(*nit)->setForeground( 1, QBrush(Qt::red) );
			}
		}
		nit++;
	}
}

void GraphExplorer::fillEdgesInfo()
{
	foreach(Structure::Link * e, g->edges){
		QTreeWidgetItem * eitem = new QTreeWidgetItem;
		eitem->setText(0, e->id);
		eitem->setText(1, "");

		// Also display link coordinates
		{
			Vec4d c1 = e->coord[0].front();
			Vec4d c2 = e->coord[1].front();
			e->property["coordinates"] = QString("C1 ( %1, %2 )   C2 ( %3, %4 )").arg(c1.x()).arg(c1.y()).arg(c2.x()).arg(c2.y());
		}

		fillInfoItem(e->property, eitem);
		ui->edgesTree->addTopLevelItem(eitem);
	}

	// Color changes when values do
	QTreeWidgetItemIterator eit(ui->edgesTree);
	while(*eit) {
		QString key = fullName(*eit).join("");
		if(oldValues.contains(key)){
			if(oldValues[key] != (*eit)->text(1)){
				(*eit)->setForeground( 0, QBrush(Qt::red) );
				(*eit)->setForeground( 1, QBrush(Qt::red) );
			}
		}
		eit++;
	}
}

void GraphExplorer::fillGraphInfo()
{
	QTreeWidgetItem * eitem = new QTreeWidgetItem;
	eitem->setText(0, "Graph");
	eitem->setText(1, "");

	g->property["groups"].setValue( g->groups );

	fillInfoItem(g->property, eitem);
	ui->graphTree->addTopLevelItem(eitem);
}

void GraphExplorer::fillInfoItem( QMap<QString,QVariant> prop, QTreeWidgetItem * item )
{
	foreach(QString key, prop.keys())
	{
		QTreeWidgetItem * p = new QTreeWidgetItem;
		QVariant val = prop[key];
		p->setText(0, key);

		// Special values
		if(key == "taskType"){
			p->setText(1, TaskNames[val.toInt()]);
		}

		if(key == "task"){
			Task * task = prop[key].value<Task*>();
			foreach(QString k, task->property.keys())
			{
				QTreeWidgetItem * taskItem = new QTreeWidgetItem;
				taskItem->setText(0, k);
				QVariant v = task->property[k];
				QString tval = v.toString();

				if(tval.size())
					taskItem->setText(1, v.toString());
				else
					taskItem->setText(1, v.typeName());

				p->addChild(taskItem);
			}
		}

		QString typeName = val.typeName();

		if(val.toString().size())
			p->setText(1, val.toString());
		else if(typeName == "Vector3")
		{
			Vector3 vec = val.value<Vector3>();
			QString vec_str = QString("[ %1, %2, %3 ]").arg(vec[0]).arg(vec[1]).arg(vec[2]);
			p->setText(1, vec_str);
		}
		else if(typeName == "QStringList")
			p->setText(1, val.value<QStringList>().join(", "));
		else if(typeName == "QVector<QString>")
		{
			QVector<QString> vec_str = val.value< QVector<QString> >();
			QStringList strs;
			foreach(QString str, vec_str) strs << str;
			p->setText(1, strs.join(", "));
		}
		else if(typeName == "Structure::NodeGroups")
		{
			QVector< QVector<QString> > groups = val.value< QVector< QVector<QString> > >();
			QStringList groupsStr;

			foreach(QVector<QString> group, groups){
				QStringList strs;
				foreach(QString str, group) strs << str;
				groupsStr << ("{" + strs.join(",") + "}");
			}

			p->setText(1, groupsStr.join(" "));
		}
		else
			p->setText(1, typeName);

		if(p->text(0).size()) 
		{
			item->addChild(p);
		}
		else
			delete p;
	}
}

GraphExplorer::~GraphExplorer()
{
    delete ui;
}

void GraphExplorer::clear()
{
	ui->graphName->setText("Loading graph..");

	ui->nodesTree->clear();
	ui->edgesTree->clear();
	ui->graphTree->clear();
}

void GraphExplorer::filterNodes()
{
	ui->nodesTree->clear();
	fillNodesInfo();
	filterTree(ui->nodesTree, ui->nodesFilterProperty->text().split("|",QString::SkipEmptyParts), 0);
	filterTree(ui->nodesTree, ui->nodesFilterValue->text().split("|",QString::SkipEmptyParts), 1);
}

void GraphExplorer::filterEdges()
{
	ui->edgesTree->clear();
	fillEdgesInfo();
	filterTree(ui->edgesTree, ui->edgesFilterProperty->text().split("|",QString::SkipEmptyParts), 0);
	filterTree(ui->edgesTree, ui->edgesFilterValue->text().split("|",QString::SkipEmptyParts), 1);
}

void GraphExplorer::filterTree(QTreeWidget * tree, QStringList filters, int column)
{
	if(!filters.size()) return;

	QSet<QTreeWidgetItem*> terminals;

	// Get all terminals
	QTreeWidgetItemIterator it(tree);
	while (*it) {
		if((*it)->childCount() == 0) terminals.insert(*it);
		it++;
	}

	// Check if terminals name is within filtered
	QVector<QTreeWidgetItem*> keep;
	foreach(QTreeWidgetItem * item, terminals)
	{
		QStringList itemName = column == 0 ? fullName(item) : (QStringList() << item->text(column));

		bool isKeep = false;

		foreach(QString n, itemName){
			foreach(QString f, filters){
				if(n.contains(f, Qt::CaseInsensitive)){
					isKeep = true;
					break;
				}
			}
			if(isKeep) break;
		}

		if(isKeep)
		{
			QTreeWidgetItem * filtered = new QTreeWidgetItem();
			filtered->setText(0, fullName(item).join(" / "));
			filtered->setText(1, item->text(1));
			// Preserve any coloration
			filtered->setForeground(0, item->foreground(0));
			filtered->setForeground(1, item->foreground(1));
			keep.push_back(filtered);
		}
	}

	tree->clear();

	foreach(QTreeWidgetItem* item, keep){
		tree->addTopLevelItem(item);
	}
}

QStringList GraphExplorer::selectedNode()
{
	return treeSelection(ui->nodesTree);
}

QStringList GraphExplorer::selectedEdge()
{
	return treeSelection(ui->edgesTree);
}

QStringList GraphExplorer::treeSelection( QTreeWidget * tree )
{
	QStringList selection;

	foreach(QTreeWidgetItem * item, tree->selectedItems()){
		QTreeWidgetItem * parent = item;

		while( parent ){
			selection << parent->text(0);
			parent = parent->parent();
		}
	}

	return selection;
}

QStringList GraphExplorer::fullName( QTreeWidgetItem * item )
{
	QStringList nameList;
	QTreeWidgetItem * parent = item;
	while( parent ){
		nameList << parent->text(0);
		parent = parent->parent();
	}
	return nameList;
}
back to top