Commit 6c8fc813 authored by Calvin Buckley's avatar Calvin Buckley 🤔 Committed by Albert Astals Cid

Use a common save dialog for exporting call graphs

This lets you pick where the saved call graph visualization should
go, and what format you wanted it in. Previously, it generated a
dot file, and on Unix, a PostScript file. This lets you choose a
dot or PS/PDF file, rendering to an intermediate dot file if needed.

This also changes the context menu in the per-function graph too,
increasing commonality in code.

More formats can be added as needed.

Possible caveats:
- QProcess might be blocking.
parent 122bba1e
...@@ -1074,17 +1074,7 @@ void TopLevel::exportGraph() ...@@ -1074,17 +1074,7 @@ void TopLevel::exportGraph()
{ {
if (!_data || !_function) return; if (!_data || !_function) return;
QString n = QStringLiteral("callgraph.dot"); GraphExporter::savePrompt(this, _data, _function, _eventType, _groupType, nullptr);
GraphExporter ge(_data, _function, _eventType, _groupType, n);
ge.writeDot();
#ifdef Q_OS_UNIX
// shell commands only work in UNIX
QString cmd = QStringLiteral("(dot %1 -Tps > %2.ps; xdg-open %3.ps)&")
.arg(n).arg(n).arg(n);
if (::system(QFile::encodeName( cmd ))<0)
qDebug() << "TopLevel::exportGraph: can not run " << cmd;
#endif
} }
......
...@@ -27,6 +27,7 @@ ...@@ -27,6 +27,7 @@
#include <QApplication> #include <QApplication>
#include <QDebug> #include <QDebug>
#include <QDesktopServices>
#include <QFile> #include <QFile>
#include <QFileDialog> #include <QFileDialog>
#include <QTemporaryFile> #include <QTemporaryFile>
...@@ -662,10 +663,10 @@ void GraphExporter::createGraph() ...@@ -662,10 +663,10 @@ void GraphExporter::createGraph()
} }
void GraphExporter::writeDot(QIODevice* device) bool GraphExporter::writeDot(QIODevice* device)
{ {
if (!_item) if (!_item)
return; return false;
QFile* file = nullptr; QFile* file = nullptr;
QTextStream* stream = nullptr; QTextStream* stream = nullptr;
...@@ -680,7 +681,7 @@ void GraphExporter::writeDot(QIODevice* device) ...@@ -680,7 +681,7 @@ void GraphExporter::writeDot(QIODevice* device)
if ( !file->open(QIODevice::WriteOnly ) ) { if ( !file->open(QIODevice::WriteOnly ) ) {
qDebug() << "Can not write dot file '"<< _dotName << "'"; qDebug() << "Can not write dot file '"<< _dotName << "'";
delete file; delete file;
return; return false;
} }
stream = new QTextStream(file); stream = new QTextStream(file);
} }
...@@ -905,6 +906,64 @@ void GraphExporter::writeDot(QIODevice* device) ...@@ -905,6 +906,64 @@ void GraphExporter::writeDot(QIODevice* device)
} }
} }
delete stream; delete stream;
return true;
}
bool GraphExporter::savePrompt(QWidget *parent, TraceData *data,
TraceFunction *function, EventType *eventType,
ProfileContext::Type groupType,
CallGraphView* cgv)
{
// More formats can be added; it's a matter of adding the filter and if
QFileDialog saveDialog(parent, QObject::tr("Export Graph"));
saveDialog.setMimeTypeFilters( {"text/vnd.graphviz", "application/pdf", "application/postscript"} );
saveDialog.setFileMode(QFileDialog::AnyFile);
saveDialog.setAcceptMode(QFileDialog::AcceptSave);
if (saveDialog.exec()) {
QString intendedName = saveDialog.selectedFiles().first();
if (intendedName.isNull() || intendedName.isEmpty())
return false;
bool wrote = false;
QString dotName, dotRenderType;
QTemporaryFile maybeTemp;
maybeTemp.open();
const QString mime = saveDialog.selectedMimeTypeFilter();
if (mime == "text/vnd.graphviz") {
dotName = intendedName;
dotRenderType = "";
}
else if (mime == "application/pdf") {
dotName = maybeTemp.fileName();
dotRenderType = "-Tpdf";
}
else if (mime == "application/postscript") {
dotName = maybeTemp.fileName();
dotRenderType = "-Tps";
}
GraphExporter ge(data, function, eventType, groupType, dotName);
if (cgv != nullptr)
ge.setGraphOptions(cgv);
wrote = ge.writeDot();
if (wrote && mime != "text/vnd.graphviz") {
QProcess proc;
proc.setStandardOutputFile(intendedName, QFile::Truncate);
proc.start("dot", { dotRenderType, dotName }, QProcess::ReadWrite);
proc.waitForFinished();
wrote = proc.exitStatus() == QProcess::NormalExit;
// Open in the default app, except for GraphViz files. The .dot
// association is usually for Microsoft Word templates and will
// open a word processor instead, which isn't what we want.
if (wrote) {
QDesktopServices::openUrl(QUrl::fromLocalFile(intendedName));
}
}
return wrote;
}
return false;
} }
void GraphExporter::sortEdges() void GraphExporter::sortEdges()
...@@ -3058,17 +3117,8 @@ void CallGraphView::contextMenuEvent(QContextMenuEvent* e) ...@@ -3058,17 +3117,8 @@ void CallGraphView::contextMenuEvent(QContextMenuEvent* e)
TraceFunction* f = activeFunction(); TraceFunction* f = activeFunction();
if (!f) return; if (!f) return;
QString n; GraphExporter::savePrompt(this, TraceItemView::data(), f, eventType(),
n = QFileDialog::getSaveFileName(this, groupType(), this);
tr("Export Graph As DOT file"),
QString(), tr("Graphviz (*.dot)"));
if (!n.isEmpty()) {
GraphExporter ge(TraceItemView::data(), f, eventType(),
groupType(), n);
ge.setGraphOptions(this);
ge.writeDot();
}
} }
else if (a == exportAsImage) { else if (a == exportAsImage) {
// write current content of canvas as image to file // write current content of canvas as image to file
......
...@@ -331,7 +331,12 @@ public: ...@@ -331,7 +331,12 @@ public:
void createGraph(); void createGraph();
// calls createGraph before dumping of not already created // calls createGraph before dumping of not already created
void writeDot(QIODevice* = nullptr); bool writeDot(QIODevice* = nullptr);
// ephemereal save dialog and exporter
static bool savePrompt(QWidget *, TraceData*, TraceFunction*,
EventType*, ProfileContext::Type,
CallGraphView*);
// to map back to structures when parsing a layouted graph // to map back to structures when parsing a layouted graph
......
...@@ -898,17 +898,7 @@ void QCGTopLevel::exportGraph() ...@@ -898,17 +898,7 @@ void QCGTopLevel::exportGraph()
{ {
if (!_data || !_function) return; if (!_data || !_function) return;
QString n = QStringLiteral("callgraph.dot"); GraphExporter::savePrompt(this, _data, _function, _eventType, _groupType, nullptr);
GraphExporter ge(_data, _function, _eventType, _groupType, n);
ge.writeDot();
#ifdef Q_OS_UNIX
// shell commands only work in UNIX
QString cmd = QStringLiteral("(dot %1 -Tps > %2.ps; xdg-open %3.ps)&")
.arg(n).arg(n).arg(n);
if (::system(QFile::encodeName( cmd ))<0)
qDebug() << "QCGTopLevel::exportGraph: can not run " << cmd;
#endif
} }
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment