diff --git a/.gitignore b/.gitignore index 606359f5ba89276aeee128424d77f2e68c1e0561..8c1fa4d04b52ba6fea8237f753362a6480187c3b 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,9 @@ # Atom editor .clang_complete +# VsCode editor +.vscode + # Compiled Object files *.slo *.lo @@ -58,6 +61,7 @@ build* # Generated files src/common/ViteConfig.hpp +generate toto.trace log.txt @@ -69,3 +73,4 @@ ui* # Binary vite +/externals/glm diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 2a148a7d7c80d9c9c3190630969e1136eae81fe5..7d2f294d91615461018ef2da9be2ceae11271f0f 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,4 +1,4 @@ -image: hpclib/hiepacs +image: hpclib/vite stages: - build diff --git a/CMakeLists.txt b/CMakeLists.txt index 7520ca984ecc3339ddb3222f2572ad034b7bbac7..bc7464896c639e4aa149cd74c121a5217c372843 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -86,21 +86,16 @@ option(USE_QT5 "Use QT5 instead of QT4" ON) if (USE_QT5) - # QUIET option disables messages if the package cannot be found. - find_package(Qt5Widgets QUIET) - if(Qt5Widgets_FOUND) - # CMake 2.8.8 or greater required for qt5 use - if("${CMAKE_VERSION}" VERSION_GREATER 2.8.7) - set(USE_QT5 ON) - endif() + set(CMAKE_AUTOMOC ON) + #set(CMAKE_AUTORCC ON) + #set(CMAKE_AUTOUIC ON) + + if(CMAKE_VERSION VERSION_LESS "3.7.0") + set(CMAKE_INCLUDE_CURRENT_DIR ON) endif() - set(CMAKE_AUTOMOC ON) - find_package(Qt5Core REQUIRED) - find_package(Qt5Xml REQUIRED) - find_package(Qt5Widgets REQUIRED) - find_package(Qt5OpenGL REQUIRED) -# find_package(Qt5UiTools REQUIRED) + # QUIET option disables messages if the package cannot be found. + find_package(Qt5 COMPONENTS Widgets Core Xml OpenGL Charts Svg QUIET) else(USE_QT5) # Try Qt4 diff --git a/README b/README index b9a8acde6bb54817796d8a70428b2deebe1f7036..b00d58780d352a546f1e19578c00fd70d41560d7 100644 --- a/README +++ b/README @@ -32,7 +32,7 @@ you can use apt-get : sudo apt-get install vite For Debian users, you may need to install several packages: -apt-get install qtbase5-dev qt5-qmake qttools5-dev +apt-get install qtbase5-dev qt5-qmake qttools5-dev libqt5charts5-dev libqt5svg5-dev Then, after Qt development files have been installed, you can compile ViTE: diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 4837f6af1cfc4d204b043ccb9ecd91dd65d9fd15..7d85f22244c495d334ba023d9d6f62074a8c14c5 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -36,7 +36,7 @@ # - MARCOUEILLE Jule # - NOISETTE Pascal # - REDONDY Arthur -# - VUCHENER Cl�ment +# - VUCHENER Clement # - (RICHART Nicolas) # # @@ -119,12 +119,14 @@ SET(VITE_HDRS parser/PajeParser/ParserPaje.hpp parser/PajeParser/ParserVite.hpp # Statistics headers - statistics/Stats_window.hpp + statistics/Statistics_window.hpp + statistics/ChartView.hpp statistics/Statistic.hpp statistics/DrawStats.hpp statistics/DrawVDiagram.hpp statistics/DrawHDiagram.hpp statistics/DrawCounter.hpp + statistics/Diagram.hpp # Plugin header plugin/Command_window.hpp plugin/Plugin_window.hpp @@ -136,11 +138,11 @@ SET(VITE_UIS interface/settings.ui interface/main_window.ui interface/option_export_window.ui - interface/stats_viewer.ui interface/global_cmd.ui interface/list_of_counter_to_export.ui interface/node_select.ui interface/interval_select.ui + interface/statistics.ui ) SET(VITE_SRCS @@ -200,7 +202,9 @@ SET(VITE_SRCS parser/PajeParser/ParserVite.cpp # Statistics code files statistics/Statistic.cpp - statistics/Stats_window.cpp + statistics/ChartView.cpp + statistics/Statistics_window.cpp + statistics/Diagram.cpp # Interface code files interface/Interface_graphic.cpp interface/Interval_select.cpp @@ -459,7 +463,7 @@ endif( APPLE ) # QT5 ############################################# IF(USE_QT5) - qt5_use_modules(vite Widgets Core Xml OpenGL UiTools) + qt5_use_modules(vite Widgets Core Xml OpenGL UiTools Charts Svg) ENDIF(USE_QT5) TARGET_LINK_LIBRARIES(vite diff --git a/src/core/Core.cpp b/src/core/Core.cpp index 559e53307932b2c970dcc8c6b7f8fda3e9c27122..2802b94d2bbb253dc33d4cd629840eb23d9aecc7 100644 --- a/src/core/Core.cpp +++ b/src/core/Core.cpp @@ -111,8 +111,6 @@ #include "parser/ParserFactory.hpp" #include "parser/ParsingThread.hpp" /* -- */ -#include "statistics/Stats_window.hpp" -/* -- */ #include "interface/Node_select.hpp" #include "interface/Interface_graphic.hpp" #include "core/Core.hpp" diff --git a/src/interface/statistics.ui b/src/interface/statistics.ui new file mode 100644 index 0000000000000000000000000000000000000000..24492ad7dd9afe5c34a2111bcc0bc54000fe985e --- /dev/null +++ b/src/interface/statistics.ui @@ -0,0 +1,234 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>Statistics</class> + <widget class="QWidget" name="Statistics"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>946</width> + <height>450</height> + </rect> + </property> + <property name="windowTitle"> + <string>Form</string> + </property> + <property name="windowOpacity"> + <double>3.000000000000000</double> + </property> + <layout class="QGridLayout" name="gridLayout"> + <item row="0" column="0"> + <layout class="QVBoxLayout" name="verticalLayout"> + <item> + <widget class="QWidget" name="widget" native="true"> + <property name="maximumSize"> + <size> + <width>16777215</width> + <height>259</height> + </size> + </property> + <layout class="QHBoxLayout" name="horizontalLayout"> + <item> + <layout class="QVBoxLayout" name="verticalLayout_2"> + <item> + <widget class="QLabel" name="label_6"> + <property name="text"> + <string>Selected node:</string> + </property> + </widget> + </item> + <item> + <widget class="QTreeWidget" name="_nodes_selected"> + <column> + <property name="text"> + <string notr="true">1</string> + </property> + </column> + </widget> + </item> + </layout> + </item> + <item> + <layout class="QFormLayout" name="formLayout"> + <item row="0" column="0"> + <widget class="QLabel" name="label"> + <property name="text"> + <string>Start time</string> + </property> + </widget> + </item> + <item row="0" column="1"> + <widget class="QSlider" name="_start_time_widget"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + </widget> + </item> + <item row="1" column="0"> + <widget class="QLabel" name="label_2"> + <property name="text"> + <string>End time</string> + </property> + </widget> + </item> + <item row="1" column="1"> + <widget class="QSlider" name="_end_time_widget"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + </widget> + </item> + <item row="2" column="0"> + <widget class="QLabel" name="label_3"> + <property name="text"> + <string>Kind of viewing</string> + </property> + </widget> + </item> + <item row="2" column="1"> + <widget class="QComboBox" name="_kind_of_diagram_box"> + <item> + <property name="text"> + <string>Horizontal Histogram</string> + </property> + </item> + <item> + <property name="text"> + <string>Vertical Histogram</string> + </property> + </item> + <item> + <property name="text"> + <string>Counter stats</string> + </property> + </item> + </widget> + </item> + <item row="3" column="0"> + <widget class="QLabel" name="label_4"> + <property name="text"> + <string>States type</string> + </property> + </widget> + </item> + <item row="3" column="1"> + <widget class="QComboBox" name="_kind_of_state_box"/> + </item> + <item row="5" column="1"> + <widget class="QLabel" name="label_5"> + <property name="text"> + <string><html><head/><body><p><span style=" font-weight:600;">Statistics:</span></p><p>Uses to display diagrams about the trace states or counters.</p></body></html></string> + </property> + </widget> + </item> + <item row="6" column="1"> + <widget class="QCheckBox" name="auto_reload_box"> + <property name="text"> + <string>Auto reload stats when zoom</string> + </property> + </widget> + </item> + <item row="7" column="1"> + <layout class="QHBoxLayout" name="horizontalLayout_2"> + <item> + <spacer name="horizontalSpacer_2"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>40</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + <item> + <widget class="QPushButton" name="reload_button"> + <property name="text"> + <string>Reload</string> + </property> + </widget> + </item> + <item> + <spacer name="horizontalSpacer_3"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>40</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + <item> + <widget class="QPushButton" name="export_button"> + <property name="text"> + <string>Export</string> + </property> + </widget> + </item> + <item> + <spacer name="horizontalSpacer"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>40</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + </layout> + </item> + <item row="7" column="0"> + <widget class="QCheckBox" name="want_stacked"> + <property name="text"> + <string>Stacked</string> + </property> + <property name="checked"> + <bool>true</bool> + </property> + </widget> + </item> + </layout> + </item> + </layout> + </widget> + </item> + <item> + <layout class="QVBoxLayout" name="verticalLayout_3"> + <item> + <layout class="QHBoxLayout" name="horizontalLayout_4"> + <item> + <layout class="QVBoxLayout" name="_chart_layout"/> + </item> + <item> + <widget class="QScrollBar" name="_y_scroll"> + <property name="orientation"> + <enum>Qt::Vertical</enum> + </property> + </widget> + </item> + </layout> + </item> + <item> + <widget class="QScrollBar" name="_x_scroll"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + </widget> + </item> + </layout> + </item> + </layout> + </item> + </layout> + </widget> + <resources/> + <connections/> +</ui> diff --git a/src/interface/vite.qrc b/src/interface/vite.qrc index 174896ea4cf32a46490d2b3de7835f071de3306c..6e640094f97de09289eb5da80dbf31a55fdb3747 100644 --- a/src/interface/vite.qrc +++ b/src/interface/vite.qrc @@ -31,8 +31,8 @@ <file>info_window.ui</file> <file>main_window.ui</file> <file>option_export_window.ui</file> - <file>stats_viewer.ui</file> <file>list_of_counter_to_export.ui</file> <file>node_select.ui</file> + <file>statistics.ui</file> </qresource> </RCC> diff --git a/src/plugin/Plugin.cpp b/src/plugin/Plugin.cpp index 18588bc3175ca0fee3d10d6aa8a572a791fca50a..409f51034ebfc9e9b9517a77f437a9e3d3ad4103 100644 --- a/src/plugin/Plugin.cpp +++ b/src/plugin/Plugin.cpp @@ -51,14 +51,14 @@ #include "trace/Entitys.hpp" #include "trace/Trace.hpp" /* -- */ -#include "statistics/Stats_window.hpp" +#include "statistics/Statistics_window.hpp" /* -- */ #include "plugin/Plugin.hpp" Plugin *Plugin::new_instance(const std::string &name) { Plugin *plugin = nullptr; - if(name == "statistics window") { - plugin = new Stats_window(); + if(name == "Statistics") { + plugin = new Statistics_window(); } return plugin; } diff --git a/src/plugin/Plugin_window.cpp b/src/plugin/Plugin_window.cpp index 5f61de0e4c726f0281b14555d59bef40251a2678..95a592b402548b84327d089979a3a2b64f9692a9 100644 --- a/src/plugin/Plugin_window.cpp +++ b/src/plugin/Plugin_window.cpp @@ -82,9 +82,9 @@ Plugin_window::Plugin_window(Core *c, QWidget *parent) _layout->addWidget(_tab_widget); _layout->addWidget(_execute_button); - QWidget *centralWidget = new QWidget(); - centralWidget->setLayout(_layout); - setCentralWidget(centralWidget); + QWidget* central_widget = new QWidget(); + central_widget->setLayout(_layout); + setCentralWidget(central_widget); QIcon icon; icon.addPixmap(QPixmap(QString::fromUtf8(":/icon/icon/vite.png")), QIcon::Normal, QIcon::Off); @@ -101,7 +101,7 @@ Plugin_window::Plugin_window(Core *c, QWidget *parent) load_shared_plugins(); // TODO load "compulsory" plugins (stats for example) - load_plugin("statistics window"); + load_plugin("Statistics"); // Make a link to the menu to open the good tab if possible (menu Preferences->Plugins->...) where ... is the plugin we want to load // mainWindow->fillPluginMenu(list<QString> names); @@ -211,7 +211,7 @@ void Plugin_window::reload_plugins() { Session::load_plugin_directories(_plugin_directories); load_shared_plugins(); - load_plugin("statistics window"); + load_plugin("Statistics"); } void Plugin_window::load_list() { diff --git a/src/statistics/ChartView.cpp b/src/statistics/ChartView.cpp new file mode 100644 index 0000000000000000000000000000000000000000..22ee2399d030948b416b3d72772c46e2ce51b311 --- /dev/null +++ b/src/statistics/ChartView.cpp @@ -0,0 +1,142 @@ +#include "ChartView.hpp" + +Chart_View::Chart_View( + QScrollBar& x_scroll, + QScrollBar& y_scroll, + QtCharts::QChart *chart, + QWidget *parent +) : QChartView(chart, parent), _x_scroll(x_scroll), _y_scroll(y_scroll) { + setFocusPolicy(Qt::FocusPolicy::StrongFocus); + + connect(&x_scroll, SIGNAL(valueChanged(int)), this, SLOT(on_x_scroll_changed(int))); + connect(&y_scroll, SIGNAL(valueChanged(int)), this, SLOT(on_y_scroll_changed(int))); +} + +void Chart_View::reset_scroll_zoom() { + _scroll_value = {0, 0}; + _factor = 1; +} + +void Chart_View::restrict_scroll(bool x, bool y) { + _restrict_scroll_x = x; + _restrict_scroll_y = y; +} + +void Chart_View::wheelEvent(QWheelEvent* event) { + _ignore_scrollbar = true; + chart()->zoomReset(); + auto rect_chart = chart()->mapFromScene(chart()->plotArea()).boundingRect(); + + _factor *= event->angleDelta().y() > 0 ? 0.5 : 2; + _factor = _factor > 1 ? 1 : _factor; + + QRectF rect = chart()->plotArea(); + QPointF c = chart()->plotArea().center(); + if (_restrict_scroll_x) { + rect.setWidth(_factor * rect.width()); + _scroll_value.setX(rect_chart.width() * (1 - _factor) / 2); + _x_scroll.setValue(50); + } + if (_restrict_scroll_y) { + rect.setHeight(_factor * rect.height()); + _scroll_value.setY(rect_chart.height() * (1 - _factor) / 2); + _y_scroll.setValue(50); + } + rect.moveCenter(c); + chart()->zoomIn(rect); + QChartView::wheelEvent(event); + _ignore_scrollbar = false; +} + +void Chart_View::mousePressEvent(QMouseEvent *event) { + if (event->button() == Qt::MiddleButton) { + QApplication::setOverrideCursor(QCursor(Qt::SizeAllCursor)); + _last_mouse_pos = event->pos(); + event->accept(); + } + + QChartView::mousePressEvent(event); +} + +void Chart_View::mouseMoveEvent(QMouseEvent *event) { + // pan the chart with a middle mouse drag + if (event->buttons() & Qt::MiddleButton) { + _ignore_scrollbar = true; + auto dPos = event->pos() - _last_mouse_pos; + + auto s_x = _restrict_scroll_x ? -dPos.x() : 0; + auto s_y = _restrict_scroll_y ? +dPos.y() : 0; + + auto new_scroll = _scroll_value + QPointF{s_x * _factor, s_y * _factor}; + auto rect = chart()->plotArea(); + auto rect_chart = chart()->mapFromScene(rect).boundingRect(); + + if ( + new_scroll.x() >= -1 && + new_scroll.y() >= -1 && + new_scroll.x() + rect_chart.width() * _factor <= rect_chart.width() + 1 && + new_scroll.y() + rect_chart.height() * _factor <= rect_chart.height() + 1 + ) { + _scroll_value += QPointF{_factor * s_x, _factor * s_y}; + + auto v = ((_scroll_value.x() - rect_chart.width() * _factor) / rect_chart.width()); + v += _factor; + v /= (1 - _factor); + + _x_scroll.setValue(v * 100); + + v = ((_scroll_value.y() - rect_chart.height() * _factor) / rect_chart.height()); + v += _factor; + v /= (1 - _factor); + _y_scroll.setValue((1 - v) * 100); + chart()->scroll(s_x, s_y); + } + + _last_mouse_pos = event->pos(); + event->accept(); + + QApplication::restoreOverrideCursor(); + _ignore_scrollbar = false; + } + + QChartView::mouseMoveEvent(event); +} + +void Chart_View::on_x_scroll_changed(int x) { + if (_ignore_scrollbar) return; + if (!_restrict_scroll_x) return; + + auto rect = chart()->plotArea(); + auto rect_chart = chart()->mapFromScene(rect).boundingRect(); + + auto min = rect_chart.left() - 1; + auto max = rect_chart.right() - rect_chart.width() * _factor + 1; + + auto v = min + (max - min) * (99 - x) * (1.f / 99.f); + + auto new_scroll = max - v; + + chart()->scroll((new_scroll - _scroll_value.x()) / _factor, 0); + _scroll_value.setX(new_scroll); +} +void Chart_View::on_y_scroll_changed(int x) { + if (_ignore_scrollbar) return; + if (!_restrict_scroll_y) return; + + auto rect = chart()->plotArea(); + auto rect_chart = chart()->mapFromScene(rect).boundingRect(); + + auto min = rect_chart.top() + rect_chart.height() * _factor - 1; + auto max = rect_chart.bottom() + 1; + + auto v = min + (max - min) * x * (1.f / 99.f); + + auto new_scroll = max - v; + + chart()->scroll(0, (new_scroll - _scroll_value.y()) / _factor); + _scroll_value.setY(new_scroll); +} + +void Chart_View::focusInEvent(QFocusEvent* event) { + emit chart_focused(); +} diff --git a/src/statistics/ChartView.hpp b/src/statistics/ChartView.hpp new file mode 100644 index 0000000000000000000000000000000000000000..f70ecb6b1738ed5cbf16833294ccca870b2e1643 --- /dev/null +++ b/src/statistics/ChartView.hpp @@ -0,0 +1,47 @@ +#pragma once + +#include <QtWidgets/qscrollbar.h> +#include <QtCharts/qchartview.h> +#include <QtWidgets/qapplication.h> +#include <QObject> + +class Chart_View : public QtCharts::QChartView { + Q_OBJECT +private: + qreal _factor=1.0; + QPoint _last_mouse_pos; + + bool _restrict_scroll_x = true; + bool _restrict_scroll_y = false; + bool _ignore_scrollbar = false; + + QScrollBar& _x_scroll; + QScrollBar& _y_scroll; + + QPointF _scroll_value; + +public: + Chart_View( + QScrollBar& x_scroll, + QScrollBar& y_scroll, + QtCharts::QChart *chart, + QWidget *parent = nullptr + ); + + void restrict_scroll(bool x, bool y); + + void reset_scroll_zoom(); + +signals: + void chart_focused(); + +protected: + virtual void focusInEvent(QFocusEvent* event) override; + + void wheelEvent(QWheelEvent* event); + void mousePressEvent(QMouseEvent *event); + void mouseMoveEvent(QMouseEvent *event); +public slots: + void on_x_scroll_changed(int x); + void on_y_scroll_changed(int x); +}; \ No newline at end of file diff --git a/src/statistics/Diagram.cpp b/src/statistics/Diagram.cpp new file mode 100644 index 0000000000000000000000000000000000000000..7fea8d3c25b5ed26ca5c75e92c52863bacbc633e --- /dev/null +++ b/src/statistics/Diagram.cpp @@ -0,0 +1,283 @@ +#include "Diagram.hpp" + +#include <unordered_map> +#include <string> + +#include <QtWidgets/qlabel.h> +#include <QtCharts/qbarseries.h> +#include <QtCharts/qchart.h> +#include <QtCharts/qbarset.h> +#include <QtCharts/qstackedbarseries.h> +#include <QtCharts/qbarcategoryaxis.h> +#include <QtCharts/qhorizontalbarseries.h> +#include <QtCharts/qhorizontalstackedbarseries.h> +#include <QtCharts/qvalueaxis.h> +#include <QtCharts/qlineseries.h> + +/* -- */ +#include "trace/values/Values.hpp" +#include "trace/EntityValue.hpp" +#include "trace/EntityTypes.hpp" +#include "trace/Entitys.hpp" +#include "trace/tree/Interval.hpp" +/* -- */ +#include "statistics/Statistic.hpp" + +// Helper function to help clean every information on the chart. +void clean_chart(QtCharts::QChart& chart) { + chart.removeAllSeries(); + auto axes = chart.axes(); + for (auto& x : axes) { + chart.removeAxis(x); + delete x; + } +} + +// We must ensure that two distinct containers have distinct names. +// So if some of them are empty we need to construct a unique name we do that by walking the +// container tree and append the name like so: root -> child1 -> grandchild1 -> ... -> leaf +std::string construct_name(Container& container, size_t max_rec = 0) { + auto parent = container.get_parent(); + std::string res; + + // >TODO(Tackwin) >Perf + // back to front string construction :( for now it's good. + while(parent && --max_rec > 0) { + auto app = parent->get_name() + " -> "; + res = app + res; + parent = parent->get_parent(); + } + + return res; +} + +// Helper function to check if a name is only whitespace. +bool name_is_whitespace(const std::string& name) { + for (auto& c : name) if (!std::isspace(c)) return false; + return true; +} + + +// Helper function that handles the logic in common for stacked_/vertical/horizontal. +// fill the series and categories with appropriate data. (Time spent on different state type for +// each containers). +void feed_abstract_bar_series( + const std::vector<Container*>& containers, + const Interval& interval, + QtCharts::QAbstractBarSeries* series, + QStringList& categories +) { + // The Qt Api dictate that a bar set contains a value for each categories + // But for us it would be more natural to says at once all the value for each categories + // so we need to transform our data a little. + + // This struct will contains the data in a QtChart friendly way. + struct Bar_Set_Data { + std::vector<double> vec; + QColor color; + }; + + std::unordered_map<std::string, Bar_Set_Data> data; + + // >Reverse + // We go end to beginning to order the containers the same way as they are in the + // principal view. + for (size_t i = containers.size() - 1; i + 1 > 0; --i) { // for each container + auto& c = containers[i]; + auto name = c->get_name(); + if (name_is_whitespace(name)) { + name = construct_name(*c) + std::to_string(i); + } + + categories << name.c_str(); // add a categorie + + Statistic stat_temp; // get the stats + c->fill_stat(&stat_temp, interval); + auto states_temp = stat_temp.get_states(); + + for (auto& state : states_temp) { // for each states type + auto h = state.second->_total_length * 100 / (interval._right - interval._left); + auto c = state.first->get_used_color(); + + // First it might be the first time that we encounter this state type so resize the + // vector. We don't care if it's done multiple time since containers.size() is a + // constant. Then add an entry in the corresponding set (meaning at index i) + data[state.first->get_name()].vec.resize(containers.size()); + data[state.first->get_name()].vec[i] = (std::size_t)(h * 10) / 10.f; + data[state.first->get_name()].color = { + (int)(255 * c->get_red()), + (int)(255 * c->get_green()), + (int)(255 * c->get_blue()) + }; + } + } + + // Then we have the data ordered by state type then by container so we can fill naturally the + // qt objects. + for (auto& it : data) { + QtCharts::QBarSet* set = new QtCharts::QBarSet(QString(it.first.c_str())); + set->setColor(it.second.color); + + // >Reverse for the same reason as before. + for (size_t i = it.second.vec.size() - 1; i + 1 > 0; --i) { + *set << it.second.vec[i]; + } + + series->append(set); + series->setLabelsVisible(true); + series->setLabelsPosition(QtCharts::QAbstractBarSeries::LabelsPosition::LabelsOutsideEnd); + } +} + +void Diagram::vertical( + QtCharts::QChart& chart, const std::vector<Container*>& containers, const Interval& interval +) { + clean_chart(chart); + + QStringList categories; + auto series = new QtCharts::QBarSeries(); + feed_abstract_bar_series(containers, interval, series, categories); + + chart.addSeries(series); + + auto *axis_x = new QtCharts::QBarCategoryAxis(); + axis_x->append(categories); + + auto *axis_y = new QtCharts::QValueAxis(); + axis_y->setRange(0, 100); + + chart.addAxis(axis_y, Qt::AlignLeft); + chart.addAxis(axis_x, Qt::AlignBottom); + + series->attachAxis(axis_x); + series->attachAxis(axis_y); +} + +void Diagram::stacked_vertical( + QtCharts::QChart& chart, const std::vector<Container*>& containers, const Interval& interval +) { + clean_chart(chart); + + QStringList categories; + auto series = new QtCharts::QStackedBarSeries(); + feed_abstract_bar_series(containers, interval, series, categories); + + chart.addSeries(series); + + auto *axis_x = new QtCharts::QBarCategoryAxis(); + axis_x->append(categories); + + auto *axis_y = new QtCharts::QValueAxis(); + axis_y->setRange(0, 100); + + chart.addAxis(axis_y, Qt::AlignLeft); + chart.addAxis(axis_x, Qt::AlignBottom); + + series->attachAxis(axis_x); + series->attachAxis(axis_y); +} + +void Diagram::horizontal( + QtCharts::QChart& chart, const std::vector<Container*>& containers, const Interval& interval +) { + clean_chart(chart); + + QStringList categories; + auto series = new QtCharts::QHorizontalBarSeries(); + feed_abstract_bar_series(containers, interval, series, categories); + + chart.addSeries(series); + + auto *axis_y = new QtCharts::QBarCategoryAxis(); + axis_y->append(categories); + + auto *axis_x = new QtCharts::QValueAxis(); + axis_x->setRange(0, 100); + + chart.addAxis(axis_y, Qt::AlignLeft); + chart.addAxis(axis_x, Qt::AlignBottom); + + series->attachAxis(axis_x); + series->attachAxis(axis_y); +} + +void Diagram::stacked_horizontal( + QtCharts::QChart& chart, const std::vector<Container*>& containers, const Interval& interval +) { + clean_chart(chart); + + QStringList categories; + auto series = new QtCharts::QHorizontalStackedBarSeries(); + feed_abstract_bar_series(containers, interval, series, categories); + + chart.addSeries(series); + + auto *axis_y = new QtCharts::QBarCategoryAxis(); + axis_y->append(categories); + + auto *axis_x = new QtCharts::QValueAxis(); + axis_x->setRange(0, 100); + + chart.addAxis(axis_y, Qt::AlignLeft); + chart.addAxis(axis_x, Qt::AlignBottom); + + series->attachAxis(axis_x); + series->attachAxis(axis_y); +} + + +void Diagram::counter( + QtCharts::QChart& chart, const std::vector<Container*>& containers, const Interval& interval +) { + clean_chart(chart); + + // we have essentially the same logic than in feed_abstract_series. + // For this line graph we want to have a "step" function since our counter are discretized but + // a line graph is continuous. + struct Line_Data { + std::vector<double> xs; + std::vector<double> ys; + }; + + std::unordered_map<std::string, Line_Data> data; + + QStringList categories; + for (size_t i = 0; i < containers.size(); ++i) { + auto& c = containers[i]; + auto name = c->get_Name().to_string(); + categories << name.c_str(); + + auto& var = *c->get_variables(); + for (auto& first_it : var) { + auto& v = first_it.second; + Line_Data d; + + // It's possible that we have more than one counter by containers so we need to + // construct an unique name. + auto name = c->get_name() + " : " + v->get_type()->get_name(); + + for (auto& second_it : *v->get_values()) { + auto& t = second_it.first; + auto& y = second_it.second; + data[name].xs.push_back(t.get_value()); + data[name].ys.push_back(y.get_value()); + } + } + } + + for (auto& it : data) { + auto* series = new QtCharts::QLineSeries(); + series->setName(QString(it.first.c_str())); + + // with this last_y and double append we construct the "step" function. + double last_y = 0; + for (size_t i = 0; i < it.second.xs.size(); ++i) { + series->append(it.second.xs[i], last_y); + last_y = it.second.ys[i]; + series->append(it.second.xs[i], it.second.ys[i]); + } + chart.addSeries(series); + } + + chart.createDefaultAxes(); +} diff --git a/src/statistics/Diagram.hpp b/src/statistics/Diagram.hpp new file mode 100644 index 0000000000000000000000000000000000000000..471122681935eee840c30e573ffe9d6909e9bad9 --- /dev/null +++ b/src/statistics/Diagram.hpp @@ -0,0 +1,53 @@ +#pragma once + +#include <vector> + +namespace QtCharts { + class QChart; +}; +class Container; +class Interval; + +namespace Diagram { + /*! + * \fn vertical(QChart& chart, const std::vector<Container*>& containers, const Interval& interval) + * \brief Will fill chart with a vertical histogram showing the relative time spent on each + * state type by containers. + */ + extern void vertical( + QtCharts::QChart& chart, const std::vector<Container*>& containers, const Interval& interval + ); + /*! + * \fn vertical(QChart& chart, const std::vector<Container*>& containers, const Interval& interval) + * \brief Will fill chart with a vertical stacked histogram showing the relative time spent on + * each state type by containers. + */ + extern void stacked_vertical( + QtCharts::QChart& chart, const std::vector<Container*>& containers, const Interval& interval + ); + + /*! + * \fn horizontal(QChart& chart, const std::vector<Container*>& containers, const Interval& interval) + * \brief Will fill chart with a horizontal histogram showing the relative time spent on each + * state type by containers. + */ + extern void horizontal( + QtCharts::QChart& chart, const std::vector<Container*>& containers, const Interval& interval + ); + /*! + * \fn horizontal(QChart& chart, const std::vector<Container*>& containers, const Interval& interval) + * \brief Will fill chart with a horizontal stacked histogram showing the relative time spent on + * each state type by containers. + */ + extern void stacked_horizontal( + QtCharts::QChart& chart, const std::vector<Container*>& containers, const Interval& interval + ); + + /*! + * \fn counter(QChart& chart, const std::vector<Container*>& containers, const Interval& interval) + * \brief Will fill chart line graph that shows the value of every counter in containers over time. + */ + extern void counter( + QtCharts::QChart& chart, const std::vector<Container*>& containers, const Interval& interval + ); +}; diff --git a/src/statistics/DrawHDiagram.hpp b/src/statistics/DrawHDiagram.hpp index 375ef5880631fe9cd1e1092ac12403a7346cca87..7bbc8045841511ef633193384263d5a0622d0ad0 100644 --- a/src/statistics/DrawHDiagram.hpp +++ b/src/statistics/DrawHDiagram.hpp @@ -248,7 +248,7 @@ public: } else { - draw_object->draw_rect(pos_x, pos_y, w, h, 0.7, 0.7, 0.75); + draw_object->draw_rect(pos_x, pos_y, w, h, 0.7f, 0.7f, 0.75f); } switch(decalage%3) { diff --git a/src/statistics/DrawVDiagram.hpp b/src/statistics/DrawVDiagram.hpp index 1c52eba917e14f0a2004cb35a85523d30e0629ee..95d9205b0a1ee7063bc64ab5eea50a7a5562b1a9 100644 --- a/src/statistics/DrawVDiagram.hpp +++ b/src/statistics/DrawVDiagram.hpp @@ -155,7 +155,7 @@ public: draw_object->draw_rect(pos_x, pos_y, _WIDTH_HISTOGRAM_DEFAULT, height/max_percentage, color->get_red(), color->get_green(), color->get_blue()); } else { - draw_object->draw_rect(pos_x, pos_y, _WIDTH_HISTOGRAM_DEFAULT, height/max_percentage, 0.7, 0.7, 0.75); + draw_object->draw_rect(pos_x, pos_y, _WIDTH_HISTOGRAM_DEFAULT, height/max_percentage, 0.7f, 0.7f, 0.75f); } // We print the percentage above @@ -203,7 +203,7 @@ public: draw_object->draw_rect(pos_x, this->_size_for_one_container-pos_y, w, h, color->get_red(), color->get_green(), color->get_blue()); } else { - draw_object->draw_rect(pos_x, pos_y, w, h, 0.7, 0.7, 0.75); + draw_object->draw_rect(pos_x, pos_y, w, h, 0.7f, 0.7f, 0.75f); } switch(decalage%3) { diff --git a/src/statistics/Statistics_window.cpp b/src/statistics/Statistics_window.cpp new file mode 100644 index 0000000000000000000000000000000000000000..e9d36ebb24e0e35b040cf9372788f421d9014820 --- /dev/null +++ b/src/statistics/Statistics_window.cpp @@ -0,0 +1,326 @@ +#include <iostream> +#include <sstream> +#include <fstream> +#include <string> +#include <map> +#include <unordered_map> +#include <set> +#include <list> +#include <stack> +#include <vector> +/* -- */ +#include <QFileDialog> // To choose the file to save +#include <QKeyEvent> +#include <QGLWidget> +/* -- */ +#include "common/common.hpp" +#include "common/Info.hpp" +#include "common/Message.hpp" +#include "interface/resource.hpp" +#include "interface/Interface.hpp" +/* -- */ +#include "trace/values/Values.hpp" +#include "trace/EntityValue.hpp" +#include "trace/EntityTypes.hpp" +#include "trace/Entitys.hpp" +#include "trace/tree/Interval.hpp" +/* -- */ +#include "render/render_stats.hpp" +#include "render/render_stats_opengl.hpp" +#include "render/render_stats_svg.hpp" +/* -- */ +#include "statistics/Statistic.hpp" +#include "statistics/DrawStats.hpp" +#include "statistics/DrawVDiagram.hpp" +#include "statistics/DrawHDiagram.hpp" +#include "statistics/DrawCounter.hpp" +#include "statistics/Statistics_window.hpp" +/* -- */ + +#include <QtWidgets/qlabel.h> + +#include <QtSvg/QSvgGenerator> + +#include "Diagram.hpp" + +Statistics_window::Statistics_window(QWidget* parent) : Plugin(parent) { + setupUi(this); + + _qt_chart = new QChart(); + _qt_chart->layout()->setContentsMargins(0, 0, 0, 0); + _qt_chart->setBackgroundRoundness(0); + _qt_chart->setTheme(QtCharts::QChart::ChartThemeDark); + + _qt_chart_view = new Chart_View(*_x_scroll, *_y_scroll, _qt_chart); + _qt_chart_view->setRenderHint(QPainter::Antialiasing); + _qt_chart_view->setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding)); + _qt_chart_view->setDragMode(QGraphicsView::ScrollHandDrag); + + _chart_layout->addWidget(_qt_chart_view); + + connect(_qt_chart_view, SIGNAL(chart_focused()), this, SLOT(auto_update_stats())); + + QMetaObject::connectSlotsByName(nullptr); +} + +void Statistics_window::set_trace(Trace* trace) { + _trace = trace; + clear(); + + if (!_trace) return; + + set_container_names(); + auto& states_types_list = *_trace->get_state_types(); + + // we populate the combo box that displays all the states type. + for (auto& it : states_types_list) { + auto std_str = it.second->get_alias(); + auto qt_str = QString::fromStdString(std_str); + _kind_of_state_box->addItem(qt_str); + } +} + + +void Statistics_window::set_container_names() { + auto root_containers = _trace->get_root_containers(); + + if(root_containers->empty()) { + auto& instance = *Message::get_instance(); + instance << tr("No containers in this trace").toStdString() << Message::ende; + return; + } + // Add the root container names + QList<QTreeWidgetItem *> items; + QFlags<Qt::ItemFlag> flg = + Qt::ItemIsUserCheckable | + Qt::ItemIsEnabled | + Qt::ItemIsSelectable | + Qt::ItemIsTristate; + + for (const auto &root_container : *root_containers) { + std::string name = root_container->get_name(); + std::string alias = root_container->get_alias(); + QStringList str_name(QString::fromStdString(name)); + QString str_alias(QString::fromStdString(alias)); + QTreeWidgetItem *current_node = new QTreeWidgetItem((QTreeWidgetItem *)nullptr, str_name); + + current_node->setFlags(flg); + current_node->setCheckState(0, Qt::Checked); + current_node->setText(1, str_alias); + items.append(current_node); + + // Recursivity to add the children names + set_container_names_rec(current_node, root_container); + } + + (*(items.begin()))->setExpanded(true); + _nodes_selected->insertTopLevelItems(0, items); +} + +void Statistics_window::set_filename(std::string filename) { + _file_viewed = std::move(filename); +} + +void Statistics_window::set_container_names_rec(QTreeWidgetItem *current_node, Container *current_container) { + const Container::Vector *children = current_container->get_children(); + QFlags<Qt::ItemFlag> flg = Qt::ItemIsUserCheckable | Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsTristate; + + for (const auto &it : *children) + { + // We create the node and we do the recursivity + std::string name = it->get_name(); + std::string alias = it->get_alias(); + QStringList str_name(QString::fromStdString(name)); + QString str_alias(QString::fromStdString(alias)); + QTreeWidgetItem *node = new QTreeWidgetItem(current_node, str_name); + node->setFlags(flg); + node->setCheckState(0,Qt::Checked); + node->setText(1, str_alias); + set_container_names_rec(node ,it); + } +} + +void Statistics_window::clear() { + _nodes_selected->clear(); + _kind_of_state_box->clear(); +} + +void Statistics_window::close_window() { + hide(); +} + +void Statistics_window::init() { + // We set the names of the containers for the tree widget + _nodes_selected->clear(); + if (_trace) set_container_names(); + + _end_time_widget->setValue(100); +} + +void Statistics_window::set_arguments(std::map<std::string /*argname*/, QVariant * /*argValue*/>) { +} + +void Statistics_window::execute() { + on_reload_button_clicked(); +} + +std::string Statistics_window::get_name() { + return "Qt Statistics window"; +} + +void Statistics_window::auto_update_stats() { + if(!_auto_reload_when_zoom) + return; + + // We update the times on the text bars + // We reload the stats + on_reload_button_clicked(); +} + +void Statistics_window::on_auto_reload_box_stateChanged(int) { + _auto_reload_when_zoom = auto_reload_box->isChecked(); +} + + +void Statistics_window::on_reload_button_clicked() { + + auto kind_of_diagram = _kind_of_diagram_box->currentIndex(); + auto kind_of_state = _kind_of_state_box->currentText(); + + // This will fill _selected_containers based on wether or not every container in the tree + // list in the ui is checked or not and is of the right type(matching with the combobox). + set_selected_nodes(kind_of_state.toStdString()); + + if(_selected_containers.empty()) { + *Message::get_instance() + << tr("You must select at least one container to view the stats").toStdString() + << Message::ende; + return ; + } + + auto range = (Info::Render::_x_max_visible - Info::Render::_x_min_visible); + auto start_time = Info::Render::_x_min_visible + _start_time_widget->value() * range / 100; + auto end_time = Info::Render::_x_min_visible + _end_time_widget->value() * range / 100; + + auto interval = Interval{start_time, end_time}; + + // Diagram::xxxxx(...) will populate the QChart. + + _qt_chart_view->reset_scroll_zoom(); + switch(kind_of_diagram) { + case Diagram_Type::Horizontal : { + _qt_chart_view->restrict_scroll(false, true); + if (_stacked_checked) + Diagram::stacked_horizontal(*_qt_chart, _selected_containers, interval); + else + Diagram::horizontal(*_qt_chart, _selected_containers, interval); + break; + } + case Diagram_Type::Vertical : { + _qt_chart_view->restrict_scroll(true, false); + if (_stacked_checked) + Diagram::stacked_vertical(*_qt_chart, _selected_containers, interval); + else + Diagram::vertical(*_qt_chart, _selected_containers, interval); + break; + } + case Diagram_Type::Counter : { + _qt_chart_view->restrict_scroll(true, true); + Diagram::counter(*_qt_chart, _selected_containers, interval); + break; + } + } + + _qt_chart->legend()->setVisible(true); + _qt_chart->legend()->setAlignment(Qt::AlignBottom); +} + +void Statistics_window::on_export_button_clicked(){ + int kind_of_diagram = _kind_of_diagram_box->currentIndex(); + QString kind_of_state = _kind_of_state_box->currentText(); + + set_selected_nodes(kind_of_state.toStdString()); + + if(_selected_containers.empty()) { + *Message::get_instance() + << tr("You must select at least one container to export the stats").toStdString() + << Message::ende; + return ; + } + + const QString path_by_default = + QString(_file_viewed.substr(0, _file_viewed.find_last_of('.')).c_str()) + ".svg"; + + QString filename = QFileDialog::getSaveFileName(this, tr("Export File"), + path_by_default, + tr("Images (*.svg)")); + + if (filename.isEmpty()) { + *Message::get_instance() + << tr("You must select a name for the file").toStdString() + << Message::ende; + return ; + } + else { + // Adding .svg to the end + if(!filename.endsWith(".svg")) { + filename += ".svg"; + } + } + + // We don't need to have a switch case for every possible type of render here because + // this will export _whatever_ is currently on screen in a .svg + QSvgGenerator generator; + generator.setFileName(filename); + generator.setSize(_qt_chart_view->size()); + generator.setViewBox(_qt_chart_view->rect()); + + QPainter painter; + painter.begin(&generator); + _qt_chart_view->render(&painter); + painter.end(); +} + +void Statistics_window::set_selected_nodes(const std::string& kind_of_state) { + const ContainerType* kind_of_container = nullptr; + + auto& states_types_list = *_trace->get_state_types(); + for (auto& it : states_types_list) { + if (it.second->get_alias() == kind_of_state) { + kind_of_container = it.second->get_container_type(); + } + } + + // We delete the previous selected containers + _selected_containers.clear(); + + // We fill the new selected containers + // TODO : Use the tree instead of the list + QTreeWidgetItemIterator it(_nodes_selected); + for (; *it; it++) { + if ((*it)->checkState(0) == Qt::Checked){ + auto str = (*it)->text(1).toStdString(); + + Container *cont = _trace->search_container(str); + if (!cont) { + continue; + } + if (cont->get_type() == kind_of_container) { + _selected_containers.push_back(cont); + } + } + } + _number_of_selected_container = _selected_containers.size(); + +#ifdef STAT_DEBUG + for(unsigned int i = 0 ; i < _selected_containers.size() ; i ++) { + cout << _selected_containers[i]->get_alias() << endl; + } +#endif +} + +void Statistics_window::on_want_stacked_stateChanged(int x) { + _stacked_checked = x == Qt::Checked; + on_reload_button_clicked(); +} + diff --git a/src/statistics/Statistics_window.hpp b/src/statistics/Statistics_window.hpp new file mode 100644 index 0000000000000000000000000000000000000000..3fd7336df73d45274cc58e7a53e72fe7aaa2d21f --- /dev/null +++ b/src/statistics/Statistics_window.hpp @@ -0,0 +1,175 @@ +/* +** This file is part of the ViTE project. +** +** This software is governed by the CeCILL-A license under French law +** and abiding by the rules of distribution of free software. You can +** use, modify and/or redistribute the software under the terms of the +** CeCILL-A license as circulated by CEA, CNRS and INRIA at the following +** URL: "http://www.cecill.info". +** +** As a counterpart to the access to the source code and rights to copy, +** modify and redistribute granted by the license, users are provided +** only with a limited warranty and the software's author, the holder of +** the economic rights, and the successive licensors have only limited +** liability. +** +** In this respect, the user's attention is drawn to the risks associated +** with loading, using, modifying and/or developing or reproducing the +** software by the user in light of its specific status of free software, +** that may mean that it is complicated to manipulate, and that also +** therefore means that it is reserved for developers and experienced +** professionals having in-depth computer knowledge. Users are therefore +** encouraged to load and test the software's suitability as regards +** their requirements in conditions enabling the security of their +** systems and/or data to be ensured and, more generally, to use and +** operate it in the same conditions as regards security. +** +** The fact that you are presently reading this means that you have had +** knowledge of the CeCILL-A license and that you accept its terms. +** +** +** ViTE developers are (for version 0.* to 1.0): +** +** - Bourroux Luca +** +*/ +/*! + *\file Stats_window.hpp + */ + +#ifndef _STATISTIC_WINDOW +#define _STATISTIC_WINDOW + +/* For moc compilation */ +#include <string> +#include <map> +#include <list> +/* -- */ +#include "trace/values/Values.hpp" +#include "trace/EntityValue.hpp" +#include "trace/EntityTypes.hpp" +#include "trace/Entitys.hpp" +#include "trace/tree/Interval.hpp" +#include "trace/Trace.hpp" +/* -- */ +#include "ui_statistics.h" +#include "plugin/Plugin.hpp" + +#include <QtWidgets/qframe.h> +#include <QtWidgets/qscrollarea.h> +#include <QtWidgets/qboxlayout.h> +#include <QtWidgets/qtreewidget.h> +#include <QtCharts/qchart.h> +#include <QChartView> +#include <QtCharts> + +#include "ChartView.hpp" + +enum Diagram_Type { + Horizontal = 0, + Vertical, + Counter +}; + +class Trace; +class Statistics_window : public Plugin, protected Ui::Statistics { + + Q_OBJECT + +private: + QChart* _qt_chart = nullptr; + Chart_View* _qt_chart_view = nullptr; + QFrame* _main_frame = nullptr; + Trace* _trace = nullptr; + + std::vector<Container*> _selected_containers; + + std::string _file_viewed; + + int _number_of_selected_container = 0; + + /** + * If we reload automatically the stats when a zoom is done (maybe look when the containers selection will be added if we do not add/remove them) + * -> a mettre dans le fichier de config + */ + bool _auto_reload_when_zoom; + bool _stacked_checked = true; +public: + Statistics_window(QWidget* parent = nullptr); + ~Statistics_window() = default; + + /*! + * \fn set_trace(Trace *trace) + * \brief Set the trace parsed (give the container names) + * \param trace The trace. + */ + void set_trace(Trace *trace) override; + + /*! + * \fn set_filename(std::string filename) + * \brief Set the name of the file trace. + * \param filename The file name. + */ + void set_filename(std::string filename); + + /** Plugin interface **/ + /*! + * \fn void set_arguments(std::map<std::string , QVariant *>); + * \brief Set the arguments of this plugin + */ + void set_arguments(std::map<std::string /*argname*/, QVariant * /*argValue*/>) override; + /*! + * \fn close_window() + * \brief Properly close the window + */ + void close_window(); + /*! + * \fn clear() + * \brief Clear the tab when switching between tabs + */ + void clear() override; + + /*! + * \fn init() + * \brief Initialize the plugin + */ + void init() override; + + /*! + * \fn execute() + * \brief Execute the plugin. Here it does the same as the reload button + */ + void execute() override; + /*! + * \fn std::string get_name(); + * \brief Return the name of the plugin + */ + std::string get_name() override; + + +private: + /*! + * \fn set_container_names() + * \brief Set the names of the containers in the tree + */ + void set_container_names(); + void set_container_names_rec(QTreeWidgetItem *current_node, Container *current_container); + + /*! + * \brief Fill the vector of selected containers depending on the ones chosen in the tree widget + */ + void set_selected_nodes(const std::string &); + +public slots: + void auto_update_stats(); + +private slots: + void on_auto_reload_box_stateChanged(int); + void on_want_stacked_stateChanged(int); + + void on_reload_button_clicked(); + void on_export_button_clicked(); + +}; + +#endif diff --git a/src/statistics/Stats_window.cpp b/src/statistics/Stats_window.cpp deleted file mode 100644 index d4133ff47fbb3a5df79f4f48994e751c78103a71..0000000000000000000000000000000000000000 --- a/src/statistics/Stats_window.cpp +++ /dev/null @@ -1,467 +0,0 @@ -/* - ** This file is part of the ViTE project. - ** - ** This software is governed by the CeCILL-A license under French law - ** and abiding by the rules of distribution of free software. You can - ** use, modify and/or redistribute the software under the terms of the - ** CeCILL-A license as circulated by CEA, CNRS and INRIA at the following - ** URL: "http://www.cecill.info". - ** - ** As a counterpart to the access to the source code and rights to copy, - ** modify and redistribute granted by the license, users are provided - ** only with a limited warranty and the software's author, the holder of - ** the economic rights, and the successive licensors have only limited - ** liability. - ** - ** In this respect, the user's attention is drawn to the risks associated - ** with loading, using, modifying and/or developing or reproducing the - ** software by the user in light of its specific status of free software, - ** that may mean that it is complicated to manipulate, and that also - ** therefore means that it is reserved for developers and experienced - ** professionals having in-depth computer knowledge. Users are therefore - ** encouraged to load and test the software's suitability as regards - ** their requirements in conditions enabling the security of their - ** systems and/or data to be ensured and, more generally, to use and - ** operate it in the same conditions as regards security. - ** - ** The fact that you are presently reading this means that you have had - ** knowledge of the CeCILL-A license and that you accept its terms. - ** - ** - ** ViTE developers are (for version 0.* to 1.0): - ** - ** - COULOMB Kevin - ** - FAVERGE Mathieu - ** - JAZEIX Johnny - ** - LAGRASSE Olivier - ** - MARCOUEILLE Jule - ** - NOISETTE Pascal - ** - REDONDY Arthur - ** - VUCHENER Clément - ** - */ -#include <iostream> -#include <sstream> -#include <fstream> -#include <string> -#include <map> -#include <set> -#include <list> -#include <stack> -#include <vector> -/* -- */ -#include <QFileDialog> // To choose the file to save -#include <QKeyEvent> -#include <QGLWidget> -/* -- */ -#include "common/common.hpp" -#include "common/Info.hpp" -#include "common/Message.hpp" -#include "interface/resource.hpp" -#include "interface/Interface.hpp" -/* -- */ -#include "trace/values/Values.hpp" -#include "trace/EntityValue.hpp" -#include "trace/EntityTypes.hpp" -#include "trace/Entitys.hpp" -#include "trace/tree/Interval.hpp" -/* -- */ -#include "render/render_stats.hpp" -#include "render/render_stats_opengl.hpp" -#include "render/render_stats_svg.hpp" -/* -- */ -#include "statistics/Statistic.hpp" -#include "statistics/DrawStats.hpp" -#include "statistics/DrawVDiagram.hpp" -#include "statistics/DrawHDiagram.hpp" -#include "statistics/DrawCounter.hpp" -#include "statistics/Stats_window.hpp" -/* -- */ -using namespace std; - -Stats_window::Stats_window(QWidget *parent) : Plugin(parent) { - setupUi(this); - - CKFP(_ui_stats_area_layout = this->findChild<QVBoxLayout *>("stats_area"), QObject::tr("Cannot find the stats_area_layout in the .ui file").toStdString()); - - _ui_stats_area = new Render_stats_opengl(this); - _ui_stats_area_layout->addWidget(_ui_stats_area); - - _x_translated = 0; - _y_translated = 0; - - _auto_reload_when_zoom = false; - - QMetaObject::connectSlotsByName(nullptr); -} - -Stats_window::~Stats_window() { - delete _ui_stats_area; -} - -void Stats_window::set_container_names() { - - const Container::Vector *root_containers = _trace->get_root_containers(); - - if(root_containers->empty()) { - *Message::get_instance() << tr("No containers in this trace").toStdString() << Message::ende; - return; - } - // Add the root container names - QList<QTreeWidgetItem *> items; - QFlags<Qt::ItemFlag> flg=Qt::ItemIsUserCheckable | Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsTristate; - - for (const auto &root_container : *root_containers) { - string name = root_container->get_name(); - string alias = root_container->get_alias(); - QStringList str_name(QString::fromStdString(name)); - QString str_alias(QString::fromStdString(alias)); - QTreeWidgetItem *current_node = new QTreeWidgetItem((QTreeWidgetItem *)nullptr, str_name); - - current_node->setFlags(flg); - current_node->setCheckState(0, Qt::Checked); - current_node->setText(1, str_alias); - items.append(current_node); - - // Recursivity to add the children names - set_container_names_rec(current_node, root_container); - } - - (*(items.begin()))->setExpanded(true); - _nodes_selected->insertTopLevelItems(0, items); -} - -void Stats_window::set_container_names_rec(QTreeWidgetItem *current_node, Container *current_container) { - const Container::Vector *children = current_container->get_children(); - QFlags<Qt::ItemFlag> flg = Qt::ItemIsUserCheckable | Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsTristate; - - for (const auto &it : *children) - { - // We create the node and we do the recursivity - string name = it->get_name(); - string alias = it->get_alias(); - QStringList str_name(QString::fromStdString(name)); - QString str_alias(QString::fromStdString(alias)); - QTreeWidgetItem *node = new QTreeWidgetItem(current_node, str_name); - node->setFlags(flg); - node->setCheckState(0,Qt::Checked); - node->setText(1, str_alias); - set_container_names_rec(node ,it); - } -} - -void Stats_window::set_filename(string filename){ - _file_viewed = std::move(filename); -} - - -void Stats_window::set_trace(Trace *trace) { - const map<std::string, StateType *> *states_types_list; - map<std::string, StateType *>::const_iterator it; - map<std::string, StateType *>::const_iterator end; - - // Initialize _trace - _trace = trace; - - // Clear the boxes - clear(); - - if ( trace != nullptr ) { - // Fill the tree - set_container_names(); - - // Fill in the combobox - states_types_list = _trace->get_state_types(); - end = states_types_list->end(); - for (it = states_types_list->begin(); - it != end; - it++){ - string alias = (*it).second->get_alias(); - QString temp(QString::fromStdString(alias)); - _kind_of_state_box->addItem(temp); - } - } -} - -void Stats_window::set_selected_nodes(const string &kind_of_state){ - const map<std::string, StateType *> *states_types_list; - map<std::string, StateType *>::const_iterator itstat; - map<std::string, StateType *>::const_iterator endstat; - const ContainerType *kind_of_container = nullptr; - - states_types_list = _trace->get_state_types(); - endstat = states_types_list->end(); - for (itstat = states_types_list->begin(); - itstat != endstat; - itstat++){ - if ((*itstat).second->get_alias() == kind_of_state){ - kind_of_container = (*itstat).second->get_container_type(); - continue; - } - } - - // We delete the previous selected containers - if(!_selected_containers.empty()) { - _selected_containers.clear(); - } - - // We fill the new selected containers - // TODO : Use the tree instead of the list - QTreeWidgetItemIterator it(_nodes_selected); - for (; *it; it++) { - if ((*it)->checkState(0) == Qt::Checked){ - Container *cont = _trace->search_container((*it)->text(1).toStdString()); - if (!cont) { - continue; - } - if (cont->get_type() == kind_of_container) { - _selected_containers.push_back(cont); - } - } - } - _number_of_selected_container = _selected_containers.size(); - -#ifdef STAT_DEBUG - for(unsigned int i = 0 ; i < _selected_containers.size() ; i ++) { - cout << _selected_containers[i]->get_alias() << endl; - } -#endif -} - - -void Stats_window::on_reload_button_clicked() { - int kind_of_diagram = _kind_of_diagram_box->currentIndex(); - QString kind_of_state = _kind_of_state_box->currentText(); - - set_selected_nodes(kind_of_state.toStdString()); - - if(_selected_containers.empty()) { - *Message::get_instance() << tr("You must select at least one container to view the stats").toStdString() << Message::ende; - return ; - } - - Reinit_scroll_bars(); - - // We get the times - _start_time = _start_time_widget->text().toDouble(); - _end_time = _end_time_widget->text().toDouble(); - - // We create a drawer - DrawStats<Render_stats_opengl> *drawer; - switch (kind_of_diagram) { - case _HDIAGRAM_INDEX: - drawer = new DrawHDiagram<Render_stats_opengl>(); - break; - case _DIAGRAM_INDEX: - drawer = new DrawVDiagram<Render_stats_opengl>(); - break; - case _COUNTER_INDEX: - drawer = new DrawCounter<Render_stats_opengl>(); - break; - default: - *Message::get_instance() << _kind_of_diagram_box->currentText().toStdString() - << tr(" not yet implemented").toStdString() << Message::endw; - drawer = new DrawHDiagram<Render_stats_opengl>(); - } - drawer->set_times(_start_time, _end_time); - drawer->build(_ui_stats_area, _selected_containers); - _ui_stats_area->updateGL(); - - delete drawer; -} - -void Stats_window::on_export_button_clicked(){ - int kind_of_diagram = _kind_of_diagram_box->currentIndex(); - QString kind_of_state = _kind_of_state_box->currentText(); - - set_selected_nodes(kind_of_state.toStdString()); - - if(_selected_containers.empty()) { - *Message::get_instance() << tr("You must select at least one container to export the stats").toStdString() << Message::ende; - return ; - } - - Reinit_scroll_bars(); - - // We get the times - _start_time = _start_time_widget->text().toDouble(); - _end_time = _end_time_widget->text().toDouble(); - - const QString path_by_default = QString(_file_viewed.substr(0, _file_viewed.find_last_of('.')).c_str()) + ".svg"; - - QString filename = QFileDialog::getSaveFileName(this, tr("Export File"), - path_by_default, - tr("Images (*.svg)")); - - if (filename.isEmpty()) { - *Message::get_instance() << tr("You must select a name for the file").toStdString() << Message::ende; - return ; - } - else { - // Adding .svg to the end - if(!filename.endsWith(".svg")) { - filename += ".svg"; - } - } - // We create a drawer - Render_stats_svg svg(filename.toStdString()); - DrawStats<Render_stats_svg> *drawer; - switch (kind_of_diagram) { - case _HDIAGRAM_INDEX: - drawer = new DrawHDiagram<Render_stats_svg>(); - break; - case _DIAGRAM_INDEX: - drawer = new DrawVDiagram<Render_stats_svg>(); - break; - case _COUNTER_INDEX: - drawer = new DrawCounter<Render_stats_svg>(); - break; - default: - *Message::get_instance() << _kind_of_diagram_box->currentText().toStdString() - << tr(" not yet implemented").toStdString() << Message::endw; - drawer = new DrawHDiagram<Render_stats_svg>(); - } - drawer->set_times(_start_time, _end_time); - drawer->build(&svg, _selected_containers); - - delete drawer; - -} - -void Stats_window::close_window(){ - _ui_stats_area->clear(); - _ui_stats_area->doneCurrent(); - hide(); -} - -void Stats_window::auto_update_stats() { - if(!_auto_reload_when_zoom) - return; - // We update the times on the text bars - _start_time = Info::Render::_x_min_visible; - _end_time = Info::Render::_x_max_visible; - - QString temp; - temp.setNum(_start_time); - _start_time_widget->setText(temp); - temp.setNum(_end_time); - _end_time_widget->setText(temp); - - // We reload the stats - on_reload_button_clicked(); -} - -void Stats_window::on_auto_reload_box_stateChanged(int) { - _auto_reload_when_zoom = auto_reload_box->isChecked(); -} - -// new_value is between 0 and 99 -void Stats_window::on_y_scroll_valueChanged(int new_value) { - _ui_stats_area->translate_y(new_value); - _y_translated = new_value; -} - -// new_value is between 0 and 99 -void Stats_window::on_x_scroll_valueChanged(int new_value) { - _ui_stats_area->translate_x(new_value); - _x_translated = new_value; -} - - -void Stats_window::Reinit_scroll_bars() { - _ui_stats_area->translate_x(0); - _ui_stats_area->translate_y(0); - x_scroll->setSliderPosition(0); - y_scroll->setSliderPosition(0); -} - -void Stats_window::keyPressEvent(QKeyEvent *event) { - switch (event->key()) { - case Qt::Key_Left: - // Key 'left' pressed. - if(_x_translated > 0) { - _ui_stats_area->translate_x(--_x_translated); - x_scroll->setSliderPosition(_x_translated); - } - break; - case Qt::Key_Right: - // Key 'right' pressed. - if(_x_translated < 99) { - _ui_stats_area->translate_x(++_x_translated); - x_scroll->setSliderPosition(_x_translated); - } - break; - case Qt::Key_Up: - // Key 'up' pressed. - if(_y_translated > 0) { - _ui_stats_area->translate_y(--_y_translated); - y_scroll->setSliderPosition(_y_translated); - } - break; - case Qt::Key_Down: - // Key 'down' pressed. - if(_y_translated < 99) { - _ui_stats_area->translate_y(++_y_translated); - y_scroll->setSliderPosition(_y_translated); - } - break; - case Qt::Key_PageUp: - // Key 'Page Up' pressed. - _ui_stats_area->translate_y(0); - y_scroll->setSliderPosition(0); - break; - case Qt::Key_PageDown: - // Key 'Page Down' pressed. - _ui_stats_area->translate_y(100); - y_scroll->setSliderPosition(100); - break; - default: - /* - * Unknown key pressed. - */ - break; - } - - event->accept(); -} - -void Stats_window::init() { - // We set the names of the containers for the tree widget - _nodes_selected->clear(); - if ( _trace != nullptr ) { - set_container_names(); - } - - // We init the times - _start_time = Info::Render::_x_min_visible; - _end_time = Info::Render::_x_max_visible; - - QString temp; - temp.setNum(_start_time); - _start_time_widget->setText(temp); - temp.setNum(_end_time); - _end_time_widget->setText(temp); - - _ui_stats_area->clear(); - Reinit_scroll_bars(); -} - -void Stats_window::clear() { - _ui_stats_area->makeCurrent(); - _nodes_selected->clear(); - _ui_stats_area->clear(); - _kind_of_state_box->clear(); - Reinit_scroll_bars(); - _ui_stats_area->doneCurrent(); -} - -void Stats_window::set_arguments(std::map<std::string /*argname*/, QVariant * /*argValue*/>) { -} - -void Stats_window::execute() { - on_reload_button_clicked(); -} - -string Stats_window::get_name() { - return "Statistics window"; -} diff --git a/src/statistics/Stats_window.hpp b/src/statistics/Stats_window.hpp deleted file mode 100644 index 5e4e7c2a6e48b5dc8cc308ad3e97a6afe67b9622..0000000000000000000000000000000000000000 --- a/src/statistics/Stats_window.hpp +++ /dev/null @@ -1,224 +0,0 @@ -/* -** This file is part of the ViTE project. -** -** This software is governed by the CeCILL-A license under French law -** and abiding by the rules of distribution of free software. You can -** use, modify and/or redistribute the software under the terms of the -** CeCILL-A license as circulated by CEA, CNRS and INRIA at the following -** URL: "http://www.cecill.info". -** -** As a counterpart to the access to the source code and rights to copy, -** modify and redistribute granted by the license, users are provided -** only with a limited warranty and the software's author, the holder of -** the economic rights, and the successive licensors have only limited -** liability. -** -** In this respect, the user's attention is drawn to the risks associated -** with loading, using, modifying and/or developing or reproducing the -** software by the user in light of its specific status of free software, -** that may mean that it is complicated to manipulate, and that also -** therefore means that it is reserved for developers and experienced -** professionals having in-depth computer knowledge. Users are therefore -** encouraged to load and test the software's suitability as regards -** their requirements in conditions enabling the security of their -** systems and/or data to be ensured and, more generally, to use and -** operate it in the same conditions as regards security. -** -** The fact that you are presently reading this means that you have had -** knowledge of the CeCILL-A license and that you accept its terms. -** -** -** ViTE developers are (for version 0.* to 1.0): -** -** - COULOMB Kevin -** - FAVERGE Mathieu -** - JAZEIX Johnny -** - LAGRASSE Olivier -** - MARCOUEILLE Jule -** - NOISETTE Pascal -** - REDONDY Arthur -** - VUCHENER Clément -** -*/ -/*! - *\file Stats_window.hpp - */ - -#ifndef STATS_WINDOW_HPP -#define STATS_WINDOW_HPP - -class Render_stats_opengl; -class Stats_window; - -/* For moc compilation */ -#include <string> -#include <map> -#include <list> -/* -- */ -#include "trace/values/Values.hpp" -#include "trace/EntityValue.hpp" -#include "trace/EntityTypes.hpp" -#include "trace/Entitys.hpp" -#include "trace/tree/Interval.hpp" -#include "trace/Trace.hpp" -/* -- */ -#include "ui_stats_viewer.h" -#include "plugin/Plugin.hpp" - - -/*! - * \enum stattype - * \brief Enumeration for the kind of chart to print stats. - * - */ -typedef enum stattype { - _HDIAGRAM_INDEX, - _DIAGRAM_INDEX, - _COUNTER_INDEX -} stattype_t; - - -/*! - * \class Stats_window - * \brief Class used to display statistics of containers. - * - */ - -class Stats_window : public Plugin, protected Ui::stats_viewer { - - Q_OBJECT - - private: - - std::string _file_viewed; - double _start_time; - double _end_time; - - /** - * If we reload automatically the stats when a zoom is done (maybe look when the containers selection will be added if we do not add/remove them) - * -> a mettre dans le fichier de config - */ - bool _auto_reload_when_zoom; - - /*! - * \brief Layout which will contain the stats area. - */ - QVBoxLayout* _ui_stats_area_layout; - - Render_stats_opengl* _ui_stats_area; - - std::vector<Container *> _selected_containers; - int _number_of_selected_container; - int _screen_width, _screen_height; - - // For the scroll bars - int _x_translated, _y_translated; - -public: - /*! - * Default constructor - * \param parent The parent widget of the window. - */ - Stats_window(QWidget *parent = nullptr); - - ~Stats_window() override; - - /*! - * \fn set_container_names() - * \brief Set the names of the containers in the tree - */ - void set_container_names(); - - /*! - * \fn set_filename(std::string filename) - * \brief Set the name of the file trace. - * \param filename The file name. - */ - void set_filename(std::string filename); - - /*! - * \fn set_trace(Trace *trace) - * \brief Set the trace parsed (give the container names) - * \param trace The trace. - */ - void set_trace(Trace *trace) override; - - /*! - * \fn init_window() - * \brief Initialise the statistic window : set the container names, - the times and clear what could have been here before - */ - void init_window(); - - /*! - * \fn Reinit_scroll_bars() - * \brief Reinitialize the scroll bar to their default values (0) - */ - void Reinit_scroll_bars(); - - /*! - * \fn close_window() - * \brief Properly close the window - */ - void close_window(); - - - /** Plugin interface **/ - /*! - * \fn void set_arguments(std::map<std::string , QVariant *>); - * \brief Set the arguments of this plugin - */ - void set_arguments(std::map<std::string /*argname*/, QVariant * /*argValue*/>) override; - /*! - * \fn clear() - * \brief Clear the tab when switching between tabs - */ - void clear() override; - - /*! - * \fn init() - * \brief Initialize the plugin - */ - void init() override; - - /*! - * \fn execute() - * \brief Execute the plugin. Here it does the same as the reload button - */ - void execute() override; - /*! - * \fn std::string get_name(); - * \brief Return the name of the plugin - */ - std::string get_name() override; - - -private: - void set_container_names_rec(QTreeWidgetItem *current_node, Container *current_container); - - /*! - * \brief Fill the vector of selected containers depending on the ones chosen in the tree widget - */ - void set_selected_nodes(const std::string &); - - - /*! - * \brief This functions receives all keyboard events. - * \param event The event triggered by the keyboard event. - */ - void keyPressEvent(QKeyEvent *event) override; - -public slots: - void auto_update_stats(); - -private slots: - void on_auto_reload_box_stateChanged(int); - - void on_reload_button_clicked(); - void on_export_button_clicked(); - - void on_y_scroll_valueChanged(int new_value); - void on_x_scroll_valueChanged(int new_value); -}; - -#endif // STATS_WINDOW_HPP