diff --git a/avogadro/qtplugins/plotpdf/plotpdf.cpp b/avogadro/qtplugins/plotpdf/plotpdf.cpp index 305cf1ec68..76399bec69 100644 --- a/avogadro/qtplugins/plotpdf/plotpdf.cpp +++ b/avogadro/qtplugins/plotpdf/plotpdf.cpp @@ -152,8 +152,16 @@ void PlotPdf::displayDialog() const char* yTitle = "g(r)"; const char* windowName = "Pair Distribution Function"; - VTK::VtkPlot::generatePlot(data, lineLabels, lineColors, xTitle, yTitle, - windowName); + if (!m_plot) + m_plot.reset(new VTK::VtkPlot); + + m_plot->setData(data); + m_plot->setWindowName(windowName); + m_plot->setXTitle(xTitle); + m_plot->setYTitle(yTitle); + m_plot->setLineLabels(lineLabels); + m_plot->setLineColors(lineColors); + m_plot->show(); } bool PlotPdf::generatePdfPattern(QtGui::Molecule& mol, PdfData& results, diff --git a/avogadro/qtplugins/plotpdf/plotpdf.h b/avogadro/qtplugins/plotpdf/plotpdf.h index c7121b40b3..d9fe4db927 100644 --- a/avogadro/qtplugins/plotpdf/plotpdf.h +++ b/avogadro/qtplugins/plotpdf/plotpdf.h @@ -23,6 +23,10 @@ class QByteArray; class QStringList; +namespace VTK { +class VtkPlot; +} + namespace Avogadro { namespace QtPlugins { @@ -71,6 +75,7 @@ private slots: QScopedPointer m_pdfOptionsDialog; QScopedPointer m_displayDialogAction; + QScopedPointer m_plot; }; inline QString PlotPdf::description() const diff --git a/avogadro/qtplugins/plotrmsd/plotrmsd.cpp b/avogadro/qtplugins/plotrmsd/plotrmsd.cpp index 6baac9211a..2795215d8c 100644 --- a/avogadro/qtplugins/plotrmsd/plotrmsd.cpp +++ b/avogadro/qtplugins/plotrmsd/plotrmsd.cpp @@ -131,8 +131,16 @@ void PlotRmsd::displayDialog() const char* yTitle = "RMSD (Angstrom)"; const char* windowName = "RMSD Curve"; - VTK::VtkPlot::generatePlot(data, lineLabels, lineColors, xTitle, yTitle, - windowName); + if (!m_plot) + m_plot.reset(new VTK::VtkPlot); + + m_plot->setData(data); + m_plot->setWindowName(windowName); + m_plot->setXTitle(xTitle); + m_plot->setYTitle(yTitle); + m_plot->setLineLabels(lineLabels); + m_plot->setLineColors(lineColors); + m_plot->show(); } void PlotRmsd::generateRmsdPattern(RmsdData& results) diff --git a/avogadro/qtplugins/plotrmsd/plotrmsd.h b/avogadro/qtplugins/plotrmsd/plotrmsd.h index b9b680a9a4..ece7f0446a 100644 --- a/avogadro/qtplugins/plotrmsd/plotrmsd.h +++ b/avogadro/qtplugins/plotrmsd/plotrmsd.h @@ -25,6 +25,10 @@ class QByteArray; class QStringList; +namespace VTK { +class VtkPlot; +} + namespace Avogadro { namespace QtPlugins { @@ -66,6 +70,7 @@ private slots: QtGui::Molecule* m_molecule; std::unique_ptr m_displayDialogAction; + QScopedPointer m_plot; }; inline QString PlotRmsd::description() const diff --git a/avogadro/qtplugins/plotxrd/plotxrd.cpp b/avogadro/qtplugins/plotxrd/plotxrd.cpp index 8fefa4060f..fb6106d3b2 100644 --- a/avogadro/qtplugins/plotxrd/plotxrd.cpp +++ b/avogadro/qtplugins/plotxrd/plotxrd.cpp @@ -150,8 +150,16 @@ void PlotXrd::displayDialog() const char* yTitle = "Intensity"; const char* windowName = "Theoretical XRD Pattern"; - VTK::VtkPlot::generatePlot(data, lineLabels, lineColors, xTitle, yTitle, - windowName); + if (!m_plot) + m_plot.reset(new VTK::VtkPlot); + + m_plot->setData(data); + m_plot->setWindowName(windowName); + m_plot->setXTitle(xTitle); + m_plot->setYTitle(yTitle); + m_plot->setLineLabels(lineLabels); + m_plot->setLineColors(lineColors); + m_plot->show(); } bool PlotXrd::generateXrdPattern(const QtGui::Molecule& mol, XrdData& results, diff --git a/avogadro/qtplugins/plotxrd/plotxrd.h b/avogadro/qtplugins/plotxrd/plotxrd.h index 98a212f154..6e58db7d21 100644 --- a/avogadro/qtplugins/plotxrd/plotxrd.h +++ b/avogadro/qtplugins/plotxrd/plotxrd.h @@ -25,6 +25,10 @@ class QByteArray; class QStringList; +namespace VTK { +class VtkPlot; +} + namespace Avogadro { namespace QtPlugins { @@ -86,6 +90,7 @@ private slots: std::unique_ptr m_xrdOptionsDialog; std::unique_ptr m_displayDialogAction; + QScopedPointer m_plot; }; inline QString PlotXrd::description() const diff --git a/avogadro/vtk/vtkplot.cpp b/avogadro/vtk/vtkplot.cpp index 41c0b70fe1..496b780fff 100644 --- a/avogadro/vtk/vtkplot.cpp +++ b/avogadro/vtk/vtkplot.cpp @@ -43,11 +43,34 @@ using std::vector; namespace Avogadro { namespace VTK { -void VtkPlot::generatePlot(const vector>& data, - const vector& lineLabels, - const vector>& lineColors, - const char* xTitle, const char* yTitle, - const char* windowName) +VtkPlot::VtkPlot() : m_widget(new QVTKOpenGLWidget) +{ + m_widget->SetRenderWindow(m_renderWindow); + + // Set up the view + m_widget->setFormat(QVTKOpenGLWidget::defaultFormat()); + m_view->SetRenderWindow(m_renderWindow); + m_view->GetRenderer()->SetBackground(1.0, 1.0, 1.0); + m_view->GetRenderWindow()->SetSize(600, 600); + + // Add the chart + m_view->GetScene()->AddItem(m_chart); + + vtkAxis* bottomAxis = m_chart->GetAxis(vtkAxis::BOTTOM); + vtkAxis* leftAxis = m_chart->GetAxis(vtkAxis::LEFT); + + // Increase the title font sizes + bottomAxis->GetTitleProperties()->SetFontSize(20); + leftAxis->GetTitleProperties()->SetFontSize(20); + + // Increase the tick font sizes + bottomAxis->GetLabelProperties()->SetFontSize(20); + leftAxis->GetLabelProperties()->SetFontSize(20); +} + +VtkPlot::~VtkPlot() = default; + +void VtkPlot::setData(const vector>& data) { if (data.size() < 2) { std::cerr << "Error in " << __FUNCTION__ @@ -55,16 +78,6 @@ void VtkPlot::generatePlot(const vector>& data, return; } - // Create a table and add the data as columns - vtkNew table; - - for (size_t i = 0; i < data.size(); ++i) { - vtkNew array; - // Unique column names are necessary to prevent vtk from crashing. - array->SetName(("Column " + std::to_string(i)).c_str()); - table->AddColumn(array); - } - // All of the rows must be equal in size currently. Otherwise, we get // a garbage plot. We may be able to improve on this in the future. size_t numRows = data[0].size(); @@ -76,70 +89,67 @@ void VtkPlot::generatePlot(const vector>& data, } } + // Erase the current table + while (m_table->GetNumberOfRows() > 0) + m_table->RemoveRow(0); + + for (size_t i = 0; i < data.size(); ++i) { + vtkNew array; + // Unique column names are necessary to prevent vtk from crashing. + array->SetName(("Column " + std::to_string(i)).c_str()); + m_table->AddColumn(array); + } + // Put the data in the table - table->SetNumberOfRows(numRows); + m_table->SetNumberOfRows(numRows); for (size_t i = 0; i < data.size(); ++i) { for (size_t j = 0; j < data[i].size(); ++j) { - table->SetValue(j, i, data[i][j]); + m_table->SetValue(j, i, data[i][j]); } } +} - // Set up the view - vtkNew renderWindow; - QVTKOpenGLWidget* widget = new QVTKOpenGLWidget(); - widget->SetRenderWindow(renderWindow); - // Hackish, but at least it won't leak - widget->setAttribute(Qt::WA_DeleteOnClose); - widget->setFormat(QVTKOpenGLWidget::defaultFormat()); - vtkNew view; - view->SetRenderWindow(renderWindow); - view->GetRenderer()->SetBackground(1.0, 1.0, 1.0); - view->GetRenderWindow()->SetSize(600, 600); - view->GetRenderWindow()->SetWindowName(windowName); - - // Add the chart - vtkNew chart; - view->GetScene()->AddItem(chart); - - vtkAxis* bottomAxis = chart->GetAxis(vtkAxis::BOTTOM); - vtkAxis* leftAxis = chart->GetAxis(vtkAxis::LEFT); +void VtkPlot::setWindowName(const char* windowName) +{ + m_view->GetRenderWindow()->SetWindowName(windowName); +} - // Set the axis titles +void VtkPlot::setXTitle(const char* xTitle) +{ + vtkAxis* bottomAxis = m_chart->GetAxis(vtkAxis::BOTTOM); bottomAxis->SetTitle(xTitle); - leftAxis->SetTitle(yTitle); - - // Increase the title font sizes - bottomAxis->GetTitleProperties()->SetFontSize(20); - leftAxis->GetTitleProperties()->SetFontSize(20); +} - // Increase the tick font sizes - bottomAxis->GetLabelProperties()->SetFontSize(20); - leftAxis->GetLabelProperties()->SetFontSize(20); +void VtkPlot::setYTitle(const char* yTitle) +{ + vtkAxis* leftAxis = m_chart->GetAxis(vtkAxis::LEFT); + leftAxis->SetTitle(yTitle); +} - // Adjust the range on the x axis - bottomAxis->SetBehavior(vtkAxis::FIXED); - bottomAxis->SetRange(data[0].front(), data[0].back()); +void VtkPlot::show() +{ + // First, clear all previous plots + m_chart->ClearPlots(); // Add the lines to the chart - for (size_t i = 1; i < data.size(); ++i) { - vtkPlot* line = chart->AddPlot(vtkChart::LINE); - line->SetInputData(table, 0, i); + for (size_t i = 1; i < m_table->GetNumberOfColumns(); ++i) { + vtkPlot* line = m_chart->AddPlot(vtkChart::LINE); + line->SetInputData(m_table, 0, i); // If we have a label for this line, set it - if (i <= lineLabels.size()) - line->SetLabel(lineLabels[i - 1]); + if (i <= m_lineLabels.size()) + line->SetLabel(m_lineLabels[i - 1]); // If we have a color for this line, set it (rgba) - if (i <= lineColors.size()) { - line->SetColor(lineColors[i - 1][0], lineColors[i - 1][1], - lineColors[i - 1][2], lineColors[i - 1][3]); + if (i <= m_lineColors.size()) { + line->SetColor(m_lineColors[i - 1][0], m_lineColors[i - 1][1], + m_lineColors[i - 1][2], m_lineColors[i - 1][3]); } line->SetWidth(2.0); } - // Start the widget, we probably want to improve this in future. - widget->show(); + m_widget->show(); } } // namespace VTK diff --git a/avogadro/vtk/vtkplot.h b/avogadro/vtk/vtkplot.h index 4c5df76744..916f1b2196 100644 --- a/avogadro/vtk/vtkplot.h +++ b/avogadro/vtk/vtkplot.h @@ -19,10 +19,19 @@ #include "avogadrovtkexport.h" +#include + #include +#include #include #include +class QVTKOpenGLWidget; +class vtkChartXY; +class vtkContextView; +class vtkGenericOpenGLRenderWindow; +class vtkTable; + namespace Avogadro { namespace VTK { @@ -32,15 +41,36 @@ namespace VTK { class AVOGADROVTK_EXPORT VtkPlot { public: - // This function can generate multiple lines on the same chart. - // data[0] is the x data, and data[i] for i != 0 is the y data for line - // i - 1. 'lineLabels' and 'lineColors' should be equal to the number of - // lines (data.size() - 1) and ordered in the same way as they are in 'data'. - static void generatePlot(const std::vector>& data, - const std::vector& lineLabels, - const std::vector>& lineColors, - const char* xTitle, const char* yTitle, - const char* windowName); + explicit VtkPlot(); + ~VtkPlot(); + + // data[0] is the x data, and data[i] for i != 0 is the y data for the + // line i != 0. + void setData(const std::vector>& data); + void setWindowName(const char* windowName); + void setXTitle(const char* xTitle); + void setYTitle(const char* yTitle); + + // 'lineLabels' and 'lineColors' should be equal to the number of lines + // (data.size() - 1) and ordered in the same way as they are in 'data'. + void setLineLabels(const std::vector& labels) + { + m_lineLabels = labels; + } + void setLineColors(const std::vector>& colors) + { + m_lineColors = colors; + } + void show(); + +private: + std::unique_ptr m_widget; + vtkNew m_table; + vtkNew m_renderWindow; + vtkNew m_view; + vtkNew m_chart; + std::vector m_lineLabels; + std::vector> m_lineColors; }; } // namespace VTK