Added zoom and automatic refresh of dependency graph view.

This commit is contained in:
Eivind Kvedalen 2015-06-14 11:46:18 +02:00 committed by wmayer
parent 1749ff2d5d
commit f99fd100e6
9 changed files with 428 additions and 74 deletions

View File

@ -200,7 +200,7 @@ void Document::writeDependencyGraphViz(std::ostream &out)
out << "}" << endl; out << "}" << endl;
} }
void Document::exportGraphviz(std::ostream& out) void Document::exportGraphviz(std::ostream& out) const
{ {
std::vector<std::string> names; std::vector<std::string> names;
names.reserve(d->objectMap.size()); names.reserve(d->objectMap.size());
@ -1437,6 +1437,8 @@ void Document::recompute()
it->second->purgeTouched(); it->second->purgeTouched();
} }
d->vertexMap.clear(); d->vertexMap.clear();
signalRecomputed(*this);
} }
const char * Document::getErrorDescription(const App::DocumentObject*Obj) const const char * Document::getErrorDescription(const App::DocumentObject*Obj) const

View File

@ -129,6 +129,7 @@ public:
Base::XMLReader&)> signalImportObjects; Base::XMLReader&)> signalImportObjects;
boost::signal<void (const std::vector<App::DocumentObject*>&, Base::Reader&, boost::signal<void (const std::vector<App::DocumentObject*>&, Base::Reader&,
const std::map<std::string, std::string>&)> signalImportViewObjects; const std::map<std::string, std::string>&)> signalImportViewObjects;
boost::signal<void (const App::Document&)> signalRecomputed;
//@} //@}
/** @name File handling of the document */ /** @name File handling of the document */
@ -141,7 +142,7 @@ public:
/// Restore the document from the file in Property Path /// Restore the document from the file in Property Path
void restore (void); void restore (void);
void exportObjects(const std::vector<App::DocumentObject*>&, std::ostream&); void exportObjects(const std::vector<App::DocumentObject*>&, std::ostream&);
void exportGraphviz(std::ostream&); void exportGraphviz(std::ostream&) const;
std::vector<App::DocumentObject*> importObjects(Base::XMLReader& reader); std::vector<App::DocumentObject*> importObjects(Base::XMLReader& reader);
/// Opens the document from its file name /// Opens the document from its file name
//void open (void); //void open (void);

View File

@ -201,6 +201,7 @@ set(Gui_MOC_HDRS
EditorView.h EditorView.h
FileDialog.h FileDialog.h
Flag.h Flag.h
GraphicsViewZoom.h
GraphvizView.h GraphvizView.h
GuiApplicationNativeEventAware.h GuiApplicationNativeEventAware.h
HelpView.h HelpView.h
@ -253,6 +254,12 @@ set(Gui_MOC_HDRS
fc_wrap_cpp(Gui_MOC_SRCS ${Gui_MOC_HDRS}) fc_wrap_cpp(Gui_MOC_SRCS ${Gui_MOC_HDRS})
#SOURCE_GROUP("Moc" FILES ${Gui_MOC_SRCS}) #SOURCE_GROUP("Moc" FILES ${Gui_MOC_SRCS})
add_custom_command(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/moc_GraphvizView-internal.cpp
COMMAND ${QT_MOC_EXECUTABLE} -o ${CMAKE_CURRENT_BINARY_DIR}/moc_GraphvizView-internal.cpp ${CMAKE_CURRENT_SOURCE_DIR}/GraphvizView.cpp
DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/GraphvizView.cpp)
set_property(SOURCE GraphvizView.cpp APPEND PROPERTY OBJECT_DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/moc_GraphvizView-internal.cpp)
SET(Gui_UIC_SRCS SET(Gui_UIC_SRCS
AboutApplication.ui AboutApplication.ui
Clipping.ui Clipping.ui
@ -915,6 +922,7 @@ SET(FreeCADGui_CPP_SRCS
Document.cpp Document.cpp
DocumentModel.cpp DocumentModel.cpp
DocumentPyImp.cpp DocumentPyImp.cpp
GraphicsViewZoom.cpp
GuiApplicationNativeEventAware.cpp GuiApplicationNativeEventAware.cpp
GuiConsole.cpp GuiConsole.cpp
Macro.cpp Macro.cpp
@ -934,6 +942,7 @@ SET(FreeCADGui_SRCS
Document.h Document.h
DocumentModel.h DocumentModel.h
FreeCADGuiInit.py FreeCADGuiInit.py
GraphicsViewZoom.h
GuiApplicationNativeEventAware.h GuiApplicationNativeEventAware.h
GuiConsole.h GuiConsole.h
InventorAll.h InventorAll.h

View File

@ -361,71 +361,9 @@ StdCmdExportGraphviz::StdCmdExportGraphviz()
void StdCmdExportGraphviz::activated(int iMsg) void StdCmdExportGraphviz::activated(int iMsg)
{ {
App::Document* doc = App::GetApplication().getActiveDocument(); App::Document* doc = App::GetApplication().getActiveDocument();
std::stringstream str; Gui::GraphvizView* view = new Gui::GraphvizView(*doc);
doc->exportGraphviz(str);
ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/Paths");
QProcess proc;
QStringList args;
args << QLatin1String("-Tpng");
#ifdef FC_OS_LINUX
QString path = QString::fromUtf8(hGrp->GetASCII("Graphviz", "/usr/bin").c_str());
#else
QString path = QString::fromUtf8(hGrp->GetASCII("Graphviz").c_str());
#endif
bool pathChanged = false;
#ifdef FC_OS_WIN32
QString exe = QString::fromAscii("\"%1/dot\"").arg(path);
#else
QString exe = QString::fromAscii("%1/dot").arg(path);
#endif
proc.setEnvironment(QProcess::systemEnvironment());
do {
proc.start(exe, args);
if (!proc.waitForStarted()) {
int ret = QMessageBox::warning(getMainWindow(),
qApp->translate("Std_ExportGraphviz","Graphviz not found"),
qApp->translate("Std_ExportGraphviz","Graphviz couldn't be found on your system.\n"
"Do you want to specify its installation path if it's already installed?"),
QMessageBox::Yes, QMessageBox::No);
if (ret == QMessageBox::No)
return;
path = QFileDialog::getExistingDirectory(Gui::getMainWindow(),
qApp->translate("Std_ExportGraphviz","Graphviz installation path"));
if (path.isEmpty())
return;
pathChanged = true;
#ifdef FC_OS_WIN32
exe = QString::fromAscii("\"%1/dot\"").arg(path);
#else
exe = QString::fromAscii("%1/dot").arg(path);
#endif
}
else {
if (pathChanged)
hGrp->SetASCII("Graphviz", (const char*)path.toUtf8());
break;
}
}
while(true);
proc.write(str.str().c_str(), str.str().size());
proc.closeWriteChannel();
if (!proc.waitForFinished())
return;
QPixmap px;
if (px.loadFromData(proc.readAll(), "PNG")) {
Gui::GraphvizView* view = new Gui::GraphvizView(px);
view->setDependencyGraph(str.str());
view->setWindowTitle(qApp->translate("Std_ExportGraphviz","Dependency graph")); view->setWindowTitle(qApp->translate("Std_ExportGraphviz","Dependency graph"));
getMainWindow()->addWindow(view); getMainWindow()->addWindow(view);
}
else {
QMessageBox::warning(getMainWindow(),
qApp->translate("Std_ExportGraphviz","Graphviz failed"),
qApp->translate("Std_ExportGraphviz","Graphviz failed to create an image file"));
}
} }
bool StdCmdExportGraphviz::isActive(void) bool StdCmdExportGraphviz::isActive(void)

View File

@ -0,0 +1,94 @@
/***************************************************************************
* Copyright (c) 2015 Pavel Strakhov <ri@idzaaus.org> *
* Copyright (c) 2015 Eivind Kvedalen <eivind@kvedalen.name> *
* *
* This file is part of the FreeCAD CAx development system. *
* *
* This library is free software; you can redistribute it and/or *
* modify it under the terms of the GNU Library General Public *
* License as published by the Free Software Foundation; either *
* version 2 of the License, or (at your option) any later version. *
* *
* This library is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU Library General Public License for more details. *
* *
* You should have received a copy of the GNU Library General Public *
* License along with this library; see the file COPYING.LIB. If not, *
* write to the Free Software Foundation, Inc., 59 Temple Place, *
* Suite 330, Boston, MA 02111-1307, USA *
* *
***************************************************************************/
/*
* Based on
*
* http://stackoverflow.com/questions/19113532/qgraphicsview-zooming-in-and-out-under-mouse-position-using-mouse-wheel
*
* Re-licensed to LGPL after having contacted original author by e-mail.
*
*/
#include "PreCompiled.h"
#ifndef _PreComp_
# include <QGraphicsView>
# include <QMouseEvent>
# include <QApplication>
# include <QScrollBar>
# include <qmath.h>
#endif
#include <GraphicsViewZoom.h>
GraphicsViewZoom::GraphicsViewZoom(QGraphicsView* view)
: QObject(view), _view(view)
{
_view->viewport()->installEventFilter(this);
_view->setMouseTracking(true);
_modifiers = Qt::ControlModifier;
_zoom_factor_base = 1.0015;
}
void GraphicsViewZoom::gentle_zoom(double factor) {
_view->scale(factor, factor);
_view->centerOn(target_scene_pos);
QPointF delta_viewport_pos = target_viewport_pos - QPointF(_view->viewport()->width() / 2.0,
_view->viewport()->height() / 2.0);
QPointF viewport_center = _view->mapFromScene(target_scene_pos) - delta_viewport_pos;
_view->centerOn(_view->mapToScene(viewport_center.toPoint()));
}
void GraphicsViewZoom::set_modifiers(Qt::KeyboardModifiers modifiers) {
_modifiers = modifiers;
}
void GraphicsViewZoom::set_zoom_factor_base(double value) {
_zoom_factor_base = value;
}
bool GraphicsViewZoom::eventFilter(QObject *object, QEvent *event) {
if (event->type() == QEvent::MouseMove) {
QMouseEvent* mouse_event = static_cast<QMouseEvent*>(event);
QPointF delta = target_viewport_pos - mouse_event->pos();
if (qAbs(delta.x()) > 5 || qAbs(delta.y()) > 5) {
target_viewport_pos = mouse_event->pos();
target_scene_pos = _view->mapToScene(mouse_event->pos());
}
} else if (event->type() == QEvent::Wheel) {
QWheelEvent* wheel_event = static_cast<QWheelEvent*>(event);
if (QApplication::keyboardModifiers() == _modifiers) {
if (wheel_event->orientation() == Qt::Vertical) {
double angle = wheel_event->delta();
double factor = qPow(_zoom_factor_base, angle);
gentle_zoom(factor);
return true;
}
}
}
Q_UNUSED(object);
return false;
}
#include "moc_GraphicsViewZoom.cpp"

View File

@ -0,0 +1,90 @@
/***************************************************************************
* Copyright (c) 2015 Pavel Strakhov <ri@idzaaus.org> *
* Copyright (c) 2015 Eivind Kvedalen <eivind@kvedalen.name> *
* *
* This file is part of the FreeCAD CAx development system. *
* *
* This library is free software; you can redistribute it and/or *
* modify it under the terms of the GNU Library General Public *
* License as published by the Free Software Foundation; either *
* version 2 of the License, or (at your option) any later version. *
* *
* This library is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU Library General Public License for more details. *
* *
* You should have received a copy of the GNU Library General Public *
* License along with this library; see the file COPYING.LIB. If not, *
* write to the Free Software Foundation, Inc., 59 Temple Place, *
* Suite 330, Boston, MA 02111-1307, USA *
* *
***************************************************************************/
/*
* Based on
*
* http://stackoverflow.com/questions/19113532/qgraphicsview-zooming-in-and-out-under-mouse-position-using-mouse-wheel
*
* Re-licensed to LGPL after having contacted original author by e-mail.
*
*/
#ifndef GRAPHICSVIEWZOOM_H
#define GRAPHICSVIEWZOOM_H
#include <QObject>
#include <QPointF>
/*!
* This class adds ability to zoom QGraphicsView using mouse wheel. The point under cursor
* remains motionless while it's possible.
*
* Note that it becomes not possible when the scene's
* size is not large enough comparing to the viewport size. QGraphicsView centers the picture
* when it's smaller than the view. And QGraphicsView's scrolls boundaries don't allow to
* put any picture point at any viewport position.
*
* When the user starts scrolling, this class remembers original scene position and
* keeps it until scrolling is completed. It's better than getting original scene position at
* each scrolling step because that approach leads to position errors due to before-mentioned
* positioning restrictions.
*
* When zommed using scroll, this class emits zoomed() signal.
*
* Usage:
*
* new GraphicsViewZoom(view);
*
* The object will be deleted automatically when the view is deleted.
*
* You can set keyboard modifiers used for zooming using set_modified(). Zooming will be
* performed only on exact match of modifiers combination. The default modifier is Ctrl.
*
* You can change zoom velocity by calling set_zoom_factor_base().
* Zoom coefficient is calculated as zoom_factor_base^angle_delta
* (see QWheelEvent::angleDelta).
* The default zoom factor base is 1.0015.
*/
class QGraphicsView;
class GraphicsViewZoom : public QObject {
Q_OBJECT
public:
GraphicsViewZoom(QGraphicsView* view);
void gentle_zoom(double factor);
void set_modifiers(Qt::KeyboardModifiers modifiers);
void set_zoom_factor_base(double value);
private:
QGraphicsView* _view;
Qt::KeyboardModifiers _modifiers;
double _zoom_factor_base;
QPointF target_scene_pos, target_viewport_pos;
bool eventFilter(QObject* object, QEvent* event);
};
#endif // GRAPHICSVIEWZOOM_H

View File

@ -24,32 +24,120 @@
#include "PreCompiled.h" #include "PreCompiled.h"
#ifndef _PreComp_ #ifndef _PreComp_
# include <QApplication>
# include <QFile> # include <QFile>
# include <QPrinter> # include <QPrinter>
# include <QPrintDialog> # include <QPrintDialog>
# include <QPrintPreviewDialog> # include <QPrintPreviewDialog>
# include <QProcess> # include <QProcess>
# include <QSvgRenderer> # include <QSvgRenderer>
#endif # include <QGraphicsSvgItem>
# include <QMessageBox>
# include <QGraphicsScene> # include <QGraphicsScene>
# include <QGraphicsView> # include <QGraphicsView>
# include <QThread>
# include <QProcess>
# include <boost/bind.hpp>
#endif
#include "GraphicsViewZoom.h"
#include "FileDialog.h" #include "FileDialog.h"
#include "GraphvizView.h" #include "GraphvizView.h"
#include "Application.h"
#include "MainWindow.h"
#include <App/Application.h> #include <App/Application.h>
#include <App/Document.h>
using namespace Gui; using namespace Gui;
namespace Gui {
GraphvizView::GraphvizView(const QPixmap& p, QWidget* parent) /**
* @brief The GraphvizWorker class
*
* Implements a QThread class that does the actual conversion from dot to
* svg. All critical communcation is done using queued signals.
*
*/
class GraphvizWorker : public QThread {
Q_OBJECT
public:
GraphvizWorker(QObject * parent = 0)
: QThread(parent)
{
proc.moveToThread(this);
}
void setData(const QByteArray & data)
{
str = data;
}
void run() {
// Write data to process
proc.write(str);
proc.closeWriteChannel();
if (!proc.waitForFinished()) {
Q_EMIT error();
quit();
}
// Emit result; it will get queued for processing in the main thread
Q_EMIT svgFileRead(proc.readAll());
}
QProcess * process() {
return &proc;
}
Q_SIGNALS:
void svgFileRead(const QByteArray & data);
void error();
private:
QProcess proc;
QByteArray str;
};
}
GraphvizView::GraphvizView(App::Document & _doc, QWidget* parent)
: MDIView(0, parent) : MDIView(0, parent)
, doc(_doc)
, nPending(0)
{ {
// Create scene
scene = new QGraphicsScene(); scene = new QGraphicsScene();
scene->addPixmap(p);
// Create item to hold the graph
svgItem = new QGraphicsSvgItem();
renderer = new QSvgRenderer(this);
svgItem->setSharedRenderer(renderer);
scene->addItem(svgItem);
// Create view and zoomer object
view = new QGraphicsView(scene, this); view = new QGraphicsView(scene, this);
zoomer = new GraphicsViewZoom(view);
zoomer->set_modifiers(Qt::NoModifier);
view->show(); view->show();
// Set central widget to view
setCentralWidget(view); setCentralWidget(view);
// Create worker thread
thread = new GraphvizWorker(this);
connect(thread, SIGNAL(finished()), this, SLOT(done()));
connect(thread, SIGNAL(error()), this, SLOT(error()));
connect(thread, SIGNAL(svgFileRead(const QByteArray &)), this, SLOT(svgFileRead(const QByteArray &)));
// Connect signal from document
recomputeConnection = _doc.signalRecomputed.connect(boost::bind(&GraphvizView::updateSvgItem, this, _1));
undoConnection = _doc.signalUndo.connect(boost::bind(&GraphvizView::updateSvgItem, this, _1));
redoConnection = _doc.signalRedo.connect(boost::bind(&GraphvizView::updateSvgItem, this, _1));
updateSvgItem(_doc);
} }
GraphvizView::~GraphvizView() GraphvizView::~GraphvizView()
@ -58,11 +146,112 @@ GraphvizView::~GraphvizView()
delete view; delete view;
} }
void GraphvizView::setDependencyGraph(const std::string& s) void GraphvizView::updateSvgItem(const App::Document &doc)
{ {
graphCode = s; nPending++;
// Skip if thread is working now
if (nPending > 1)
return;
ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/Paths");
QProcess * proc = thread->process();
QStringList args;
args << QLatin1String("-Tsvg");
#ifdef FC_OS_LINUX
QString path = QString::fromUtf8(hGrp->GetASCII("Graphviz", "/usr/bin").c_str());
#else
QString path = QString::fromUtf8(hGrp->GetASCII("Graphviz").c_str());
#endif
bool pathChanged = false;
#ifdef FC_OS_WIN32
QString exe = QString::fromAscii("\"%1/dot\"").arg(path);
#else
QString exe = QString::fromAscii("%1/dot").arg(path);
#endif
proc->setEnvironment(QProcess::systemEnvironment());
do {
proc->start(exe, args);
if (!proc->waitForStarted()) {
int ret = QMessageBox::warning(Gui::getMainWindow(),
qApp->translate("Std_ExportGraphviz","Graphviz not found"),
qApp->translate("Std_ExportGraphviz","Graphviz couldn't be found on your system.\n"
"Do you want to specify its installation path if it's already installed?"),
QMessageBox::Yes, QMessageBox::No);
if (ret == QMessageBox::No) {
disconnectSignals();
return;
}
path = QFileDialog::getExistingDirectory(Gui::getMainWindow(),
qApp->translate("Std_ExportGraphviz","Graphviz installation path"));
if (path.isEmpty()) {
disconnectSignals();
return;
}
pathChanged = true;
#ifdef FC_OS_WIN32
exe = QString::fromAscii("\"%1/dot\"").arg(path);
#else
exe = QString::fromAscii("%1/dot").arg(path);
#endif
}
else {
if (pathChanged)
hGrp->SetASCII("Graphviz", (const char*)path.toUtf8());
break;
}
}
while(true);
// Create graph in dot format
std::stringstream stream;
doc.exportGraphviz(stream);
graphCode = stream.str();
// Update worker thread, and start it
thread->setData(QByteArray(graphCode.c_str(), graphCode.size()));
thread->start();
} }
void GraphvizView::svgFileRead(const QByteArray & data)
{
// Update renderer with new SVG file, and give message if something went wrong
if (renderer->load(data))
svgItem->setSharedRenderer(renderer);
else {
QMessageBox::warning(getMainWindow(),
qApp->translate("Std_ExportGraphviz","Graphviz failed"),
qApp->translate("Std_ExportGraphviz","Graphviz failed to create an image file"));
disconnectSignals();
}
}
void GraphvizView::error()
{
// If the worker fails for some reason, stop giving it more data later
disconnectSignals();
}
void GraphvizView::done()
{
nPending--;
if (nPending > 0) {
nPending = 0;
updateSvgItem(doc);
thread->start();
}
}
void GraphvizView::disconnectSignals()
{
recomputeConnection.disconnect();
undoConnection.disconnect();
redoConnection.disconnect();
}
#include <QObject>
#include <QGraphicsView>
QByteArray GraphvizView::exportGraph(const QString& format) QByteArray GraphvizView::exportGraph(const QString& format)
{ {
ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/Paths"); ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/Paths");
@ -217,3 +406,4 @@ void GraphvizView::printPreview()
} }
#include "moc_GraphvizView.cpp" #include "moc_GraphvizView.cpp"
#include "moc_GraphvizView-internal.cpp"

View File

@ -25,21 +25,29 @@
#define GUI_GRAPHVIZVIEW_H #define GUI_GRAPHVIZVIEW_H
#include "MDIView.h" #include "MDIView.h"
#include <boost/shared_ptr.hpp>
#include <boost/signals.hpp>
class QGraphicsScene; class QGraphicsScene;
class QGraphicsView; class QGraphicsView;
class QSvgRenderer;
class QGraphicsSvgItem;
class GraphicsViewZoom;
namespace Gui namespace Gui
{ {
class GraphvizWorker;
class GuiExport GraphvizView : public MDIView class GuiExport GraphvizView : public MDIView
{ {
Q_OBJECT Q_OBJECT
public: public:
GraphvizView(const QPixmap&, QWidget* parent=0); GraphvizView(App::Document &_doc, QWidget* parent=0);
~GraphvizView(); ~GraphvizView();
void setDependencyGraph(const std::string&);
QByteArray exportGraph(const QString& filter); QByteArray exportGraph(const QString& filter);
/// Message handler /// Message handler
@ -57,10 +65,29 @@ public:
virtual void printPreview(); virtual void printPreview();
//@} //@}
private Q_SLOTS:
void svgFileRead(const QByteArray & data);
void error();
void done();
private: private:
void updateSvgItem(const App::Document &doc);
void disconnectSignals();
const App::Document& doc;
std::string graphCode; std::string graphCode;
QGraphicsScene* scene; QGraphicsScene* scene;
QGraphicsView* view; QGraphicsView* view;
GraphicsViewZoom* zoomer;
QGraphicsSvgItem* svgItem;
QSvgRenderer* renderer;
GraphvizWorker* thread;
int nPending;
typedef boost::BOOST_SIGNALS_NAMESPACE::scoped_connection Connection;
Connection recomputeConnection;
Connection undoConnection;
Connection redoConnection;
}; };
} // namespace Gui } // namespace Gui

View File

@ -169,5 +169,8 @@
//#include <QWebSettings> //#include <QWebSettings>
#endif #endif
#include "qmath.h"
#include <QGraphicsView>
#include <QGraphicsScene>
#endif #endif