diff --git a/common_utils/common_utils.cpp b/common_utils/common_utils.cpp index 502a970..dd861ef 100644 --- a/common_utils/common_utils.cpp +++ b/common_utils/common_utils.cpp @@ -236,7 +236,7 @@ bool make_string(const string& src, int find_separator(const std::string& src, char& separator) { - std::vector ignored_chars = {'+', '-', 'e', '.', '\r', ','}; + std::vector ignored_chars = {'+', '-', 'e', 'E', '.', '\r', ','}; std::set unique_chars; bool is_service_char = false; bool is_dot_present = false; diff --git a/davis_one/davis.cpp b/davis_one/davis.cpp index 1a9c56a..0bbfdc2 100644 --- a/davis_one/davis.cpp +++ b/davis_one/davis.cpp @@ -355,6 +355,79 @@ const char kNoFileFoundedPage[] = R"( extern const char kWarningIcon[] = R"davis_delimeter()davis_delimeter"; + + +extern const char kHtmlDateTimeModel[] = R"davis_delimeter( + + + +
+ + + +)davis_delimeter"; + + + +extern const char kHtmlMultiChartBlock[] = R"davis_delimeter( +var trace%1 = { + x: [%2], + y: [%3], + type: 'scatter' +}; +)davis_delimeter"; + + + + +extern const char kHtmlMultiChartModel[] = R"davis_delimeter( + + + +
+ + +)davis_delimeter"; + + // *INDENT-ON* } // namespace dvs end @@ -584,7 +657,7 @@ bool make_string(const string& src, int find_separator(const std::string& src, char& separator) { - std::vector ignored_chars = {'+', '-', 'e', '.', '\r', ','}; + std::vector ignored_chars = {'+', '-', 'e', 'E', '.', '\r', ','}; std::set unique_chars; bool is_service_char = false; bool is_dot_present = false; @@ -990,6 +1063,37 @@ void showMatrixSizesAreNotTheSame(int badRow) { text); } +void showDateTimeChart(const string& date_time_values, + const vector& yValues) { + + string out; + string davis_dir; +#ifdef _WIN32 + davis_dir = "\\davis_htmls"; +#elif __linux__ + davis_dir = "/davis_htmls"; +#endif + vectorargs {ARGS_DATE_TIME_PAGE_SIZE, ""}; + args[ARG_JS_NAME] = kPlotlyJsName; + args[ARG_DATE_TIME_VALUES] = date_time_values; + + std::string values; + for (size_t i = 0; i < yValues.size(); ++i) { + std::string value = std::to_string(yValues[i]); + values.append(value); + if (i != yValues.size() - 1) { + values.append(","); + } + } + + args[ARG_Y_DATE_TIME_VALUES] = values; + make_string(kHtmlDateTimeModel, args, out); + saveStringToFile(kReportPagePath, out); + openFileBySystem(kReportPagePath); + + +} + } // namespace dvs end diff --git a/davis_one/davis.h b/davis_one/davis.h index c41f17b..ce5c89f 100644 --- a/davis_one/davis.h +++ b/davis_one/davis.h @@ -139,6 +139,22 @@ enum ARGS_REPORT_PAGE_INDEX { ARGS_REPORT_PAGE_SIZE }; +enum ARGS_DATE_TIME_PAGE_INDEX { + ARG_JS_NAME, //%1 + ARG_DATE_TIME_VALUES, //%2 + ARG_Y_DATE_TIME_VALUES, //%3 + // ADD NEW ENUM BEFORE THIS COMMENT + ARGS_DATE_TIME_PAGE_SIZE +}; + +enum ARGS_MULTI_CHARTS_PAGE { + ARG_JS_MC_NAME, + ARG_TRACES_BLOCKS, + ARG_DATA_OF_TRACES, + // ADD NEW ENUM BEFORE THIS COMMENT + ARGS_MULTI_CHARTS_PAGE_SIZE +}; + extern const char kHtmlModel[]; extern const char kColorMapDefaultPart[]; @@ -160,6 +176,13 @@ extern const char kNoFileFoundedPage[]; extern const char kWarningIcon[]; +extern const char kHtmlDateTimeModel[]; + + +extern const char kHtmlMultiChartBlock[]; +extern const char kHtmlMultiChartModel[]; + + } // namespace dvs end namespace dvs { @@ -332,6 +355,9 @@ void showReportFileEmpty(); void showMatrixSizesAreNotTheSame(int badRow); +void showDateTimeChart(const string& date_time_values, + const vector& yValues); + } // namespace dvs end diff --git a/gui/CMakeLists.txt b/gui/CMakeLists.txt index 8e3172a..0381f39 100644 --- a/gui/CMakeLists.txt +++ b/gui/CMakeLists.txt @@ -43,8 +43,12 @@ set(PROJECT_SOURCES davis_gui.h about_window.h about_window.cpp + animated_button.h + animated_button.cpp davis_gui.ui about_window.ui + json_utils.h + json_utils.cpp ) qt5_add_resources(PROJECT_SOURCES res.qrc) diff --git a/gui/about_window.ui b/gui/about_window.ui index 3a797c9..30d99b8 100644 --- a/gui/about_window.ui +++ b/gui/about_window.ui @@ -97,7 +97,7 @@ p, li { white-space: pre-wrap; } - <html><head/><body><p align="center"><span style=" font-size:12pt;">Read documentation at our </span><a href="https://github.com/DevToolsOrganization/matrix-data-visualization-DAVIS/wiki"><span style=" font-size:12pt; text-decoration: underline; color:#75bcff;">WIKI</span></a></p></body></html> + <html><head/><body><p align="center"><span style=" font-size:12pt;">Read </span><a href="https://devtoolsorganization.github.io/matrix-data-visualization-DAVIS/#/"><span style=" font-size:12pt; text-decoration: underline; color:#75bcff;">online documentation</span></a></p></body></html> Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse diff --git a/gui/animated_button.cpp b/gui/animated_button.cpp new file mode 100644 index 0000000..dfc1e55 --- /dev/null +++ b/gui/animated_button.cpp @@ -0,0 +1,74 @@ +#include "animated_button.h" +#include +#include +#include + +QString buttonStyle( + "QPushButton {" + " background-color: %1;" + " border: none;" + " color:white;" // text color + " text-align: center;" + " font-size: 13px;" + " border-radius: 14px;" + "}" +); + + +AnimatedButton::AnimatedButton(const QString& text, QColor startColor, QColor endColor, QWidget* parent) : QPushButton(text, parent) { + setStyleSheet(buttonStyle.arg(startColor.name())); + m_startColor = startColor; + m_endColor = endColor; + connect(this, &QPushButton::pressed, this, &AnimatedButton::animateButtonPress); + connect(this, &QPushButton::released, this, &AnimatedButton::animateButtonRelease); +} + +void AnimatedButton::enterEvent(QEvent* event) { + QPropertyAnimation* animation = new QPropertyAnimation(this, "backgroundColor"); + animation->setDuration(250); + animation->setStartValue(m_startColor); + animation->setEndValue(m_endColor); + animation->start(QAbstractAnimation::DeleteWhenStopped); + QPushButton::enterEvent(event); + QPushButton::enterEvent(event); +} + +void AnimatedButton::leaveEvent(QEvent* event) { + QPropertyAnimation* animation = new QPropertyAnimation(this, "backgroundColor"); + animation->setDuration(250); + animation->setStartValue(m_endColor); + animation->setEndValue(m_startColor); + animation->start(QAbstractAnimation::DeleteWhenStopped); + QPushButton::leaveEvent(event); +} + +void AnimatedButton::animateButtonPress() { + QPropertyAnimation* animation = new QPropertyAnimation(this, "geometry"); + animation->setDuration(100); + animation->setStartValue(geometry()); + animation->setEndValue(QRect(m_originalGeometry.x(), m_originalGeometry.y() + 5, + m_originalGeometry.width(), m_originalGeometry.height())); + animation->start(QAbstractAnimation::DeleteWhenStopped); +} + +void AnimatedButton::animateButtonRelease() { + QPropertyAnimation* animation = new QPropertyAnimation(this, "geometry"); + animation->setDuration(100); + animation->setStartValue(geometry()); + animation->setEndValue(m_originalGeometry); + animation->start(QAbstractAnimation::DeleteWhenStopped); +} + + +void AnimatedButton::setOriginalGeometry(const QRect& newOriginalGeometry) { + m_originalGeometry = newOriginalGeometry; +} + +QColor AnimatedButton::backgroundColor() const { + return m_backgroundColor; +} + +void AnimatedButton::setBackgroundColor(const QColor& color) { + m_backgroundColor = color; + setStyleSheet(buttonStyle.arg(color.name())); +} diff --git a/gui/animated_button.h b/gui/animated_button.h new file mode 100644 index 0000000..1e5ea89 --- /dev/null +++ b/gui/animated_button.h @@ -0,0 +1,42 @@ +#ifndef ANIMATED_BUTTON_H +#define ANIMATED_BUTTON_H +#include +#include +#include +#include +#include + +class AnimatedButton : public QPushButton { + Q_OBJECT + Q_PROPERTY(QColor backgroundColor READ backgroundColor WRITE setBackgroundColor) + + public: + AnimatedButton(const QString& text, QColor startColor, QColor endColor, QWidget* parent = nullptr); + + void setOriginalGeometry(const QRect& newOriginalGeometry); + + QColor backgroundColor() const; + + void setBackgroundColor(const QColor& color); + + protected: + void enterEvent(QEvent* event) override; + + void leaveEvent(QEvent* event) override; + + private slots: + void animateButtonPress(); + + void animateButtonRelease(); + + private: + QColor m_startColor; + QColor m_endColor; + QColor m_backgroundColor; + QRect m_originalGeometry; +}; + + + + +#endif // ANIMATED_BUTTON_H diff --git a/gui/date_time_formats.json b/gui/date_time_formats.json new file mode 100644 index 0000000..1a749c2 --- /dev/null +++ b/gui/date_time_formats.json @@ -0,0 +1,6 @@ +[ + "dd/MM/yyyy hh:mm", + "yyyy/MM/dd hh:mm:ss", + "yyyy.MM.dd_hh:mm:ss", + "yyyy/MM/dd hh_mm_ss" +] diff --git a/gui/davis_gui.cpp b/gui/davis_gui.cpp index b4eb9bd..1ca1673 100644 --- a/gui/davis_gui.cpp +++ b/gui/davis_gui.cpp @@ -16,19 +16,40 @@ #include "QFileDialog" #include "QTextStream" #include +#include +#include +#include +#include +#include +#include +#include "QDateTime" +#include +#include "json_utils.h" + + +const int ANIMATION_DURATION = 300; + DavisGUI::DavisGUI(QWidget* parent) : QMainWindow(parent) - , ui(new Ui::DavisGUI), - m_copy_paste_action(new QAction("Вставить из буфера обмена")) { + , ui(new Ui::DavisGUI) { isAboutWindowShowed = false; + m_isMinStyleWindow = false; ui->setupUi(this); - ui->centralwidget->addAction(m_copy_paste_action); this->setAcceptDrops(true); QHBoxLayout* hbl = ui->horizontalLayout_menu; QMenuBar* mb = new QMenuBar; + QString menuStyle( + "QMenuBar {" + " background-color: transparent;" + " font-size: 13px;" + "}" + "QMenuBar:hover {" + " background-color: rgb(42, 130, 218);" + "}" + ); QMenu* menu_root = new QMenu("Menu"); - QAction* action_help = new QAction("About"); + mb->setStyleSheet(menuStyle); action_surface = new QAction("surface"); action_surface->setCheckable(true); action_heatmap = new QAction("heatmap"); @@ -36,39 +57,191 @@ DavisGUI::DavisGUI(QWidget* parent) action_heatmap->setChecked(true); connect(action_heatmap, &QAction::triggered, [this]() {action_surface->setChecked(false);}); connect(action_surface, &QAction::triggered, [this]() {action_heatmap->setChecked(false);}); - connect(action_help, SIGNAL(triggered()), this, SLOT(showAboutWindow())); QMenu* menu_view = new QMenu("View"); + menu_view->setStyleSheet(menuStyle); menu_view->addAction(action_surface); menu_view->addAction(action_heatmap); - mb->setFixedSize(QSize(50, 20)); - mb->setStyleSheet("background-color:rgb(82,82,82);"); + mb->setFixedSize(QSize(50, 25)); + //mb->setStyleSheet("background-color:rgb(82,82,82);"); menu_root->addMenu(menu_view); - menu_root->addAction(action_help); mb->addMenu(menu_root); + hbl->addWidget(mb); + hbl->addItem(new QSpacerItem(2, 25, QSizePolicy::Expanding, QSizePolicy::Expanding)); - hbl->addWidget(mb); - hbl->addItem(new QSpacerItem(2, 20, QSizePolicy::Expanding, QSizePolicy::Expanding)); + QString buttonStyle( + "QPushButton {" + " background-color: none;" + " border: none;" + " font-size: 13px;" + "}" + "QPushButton:hover {" + " background-color: rgb(42, 130, 218);" + "}" + ); + QPushButton* qpbAbout = new QPushButton; + qpbAbout->setFlat(true); + qpbAbout->setStyleSheet(buttonStyle); + qpbAbout->setToolTip("About"); + connect(qpbAbout, &QPushButton::clicked, this, &DavisGUI::showAboutWindow); + qpbAbout->setFixedSize(QSize(25, 25)); + qpbAbout->setText("?"); + hbl->addWidget(qpbAbout); + + QPushButton* qpbMinMaxSize = new QPushButton; + qpbMinMaxSize->setFlat(true); + qpbMinMaxSize->setStyleSheet(buttonStyle); + qpbMinMaxSize->setToolTip("Full/compact size"); + connect(qpbMinMaxSize, &QPushButton::clicked, [this]() { + m_isMinStyleWindow = !m_isMinStyleWindow; + if (m_isMinStyleWindow) + setMinStyleWindow(ANIMATION_DURATION); + else + setMaxStyleWindow(ANIMATION_DURATION); + }); + qpbMinMaxSize->setFixedSize(QSize(25, 25)); + qpbMinMaxSize->setText("◰"); + hbl->addWidget(qpbMinMaxSize); + QPushButton* qpbMinim = new QPushButton; + qpbMinim->setFlat(true); + qpbMinim->setStyleSheet(buttonStyle); + qpbMinim->setToolTip("Minimize"); connect(qpbMinim, &QPushButton::clicked, [this]() {this->showMinimized();}); - qpbMinim->setFixedSize(QSize(20, 20)); + qpbMinim->setFixedSize(QSize(25, 25)); qpbMinim->setText("─"); hbl->addWidget(qpbMinim); + + QString buttonStyleExit( + "QPushButton {" + " background-color: none;" + " border: none;" + " font-size: 13px;" + "}" + "QPushButton:hover {" + " background-color: rgb(218, 42, 42);" + "}" + ); QPushButton* qpbExit = new QPushButton; + qpbExit->setFlat(true); + qpbExit->setStyleSheet(buttonStyleExit); + qpbExit->setToolTip("Close"); connect(qpbExit, &QPushButton::clicked, [this]() {this->close();}); - qpbExit->setFixedSize(QSize(20, 20)); + qpbExit->setFixedSize(QSize(25, 25)); qpbExit->setText("✕"); hbl->addWidget(qpbExit); this->setWindowFlags(Qt::Window | Qt::FramelessWindowHint | Qt::WindowStaysOnTopHint); - connect(m_copy_paste_action, SIGNAL(triggered()), SLOT(pasteTextAdded())); + + qpbOpen = new AnimatedButton("Open", QColor(120, 120, 120), QColor(42, 130, 218), this); + qpbOpen->setGeometry(70, 180, 90, 30); + qpbOpen->setOriginalGeometry(qpbOpen->geometry()); + + + qpbBuffer = new AnimatedButton("Copy from buffer or Ctrl+V", + QColor(120, 120, 120), + QColor(42, 130, 218), + this); + qpbBuffer->setGeometry(170, 180, 170, 30); + qpbBuffer->setOriginalGeometry(qpbBuffer->geometry()); + + connect(qpbOpen, &QPushButton::released, this, &DavisGUI::selectAndShowFiles); + connect(qpbBuffer, &QPushButton::released, this, &DavisGUI::pasteFromClipboard); } DavisGUI::~DavisGUI() { delete ui; } +void DavisGUI::show() { + QMainWindow::show(); + setMaxStyleWindow(0); +} + +void DavisGUI::hideElementsDuringResize() { + ui->label_doc->setVisible(false); + ui->label_arrow->setVisible(false); + ui->label_graph->setVisible(false); + ui->label_text->setVisible(false); + qpbBuffer->setVisible(false); + qpbOpen->setVisible(false); + update(); +} + +void DavisGUI::setMaxStyleWindow(int animDuration) { + m_isMinStyleWindow = false; + hideElementsDuringResize(); + QPropertyAnimation* animationFrame = new QPropertyAnimation(ui->frame_panel, "geometry"); + animationFrame->setEasingCurve(QEasingCurve::InOutQuad); + animationFrame->setDuration(animDuration); + animationFrame->setStartValue(ui->frame_panel->geometry()); + animationFrame->setEndValue(QRect(0, 0, 397, 25)); + QPropertyAnimation* animation = new QPropertyAnimation(this, "geometry"); + animation->setDuration(animDuration); + animation->setEasingCurve(QEasingCurve::InOutQuad); + animation->setStartValue(this->geometry()); + int xOld = this->geometry().x(); + int yOld = this->geometry().y(); + int newWidth = 397; + int newHeight = 370; + int deltaW = newWidth - this->geometry().width(); + animation->setEndValue(QRect(xOld - deltaW, yOld, newWidth, newHeight)); + connect(animation, &QPropertyAnimation::finished, this, [this]() { + ui->label_doc->setVisible(true); + ui->label_arrow->setVisible(true); + ui->label_graph->setVisible(true); + ui->label_text->setVisible(true); + ui->frame_panel->setVisible(true); + qpbBuffer->setVisible(true); + qpbOpen->setVisible(true); + ui->label_doc->setGeometry(90, 60, 91, 91); + ui->label_arrow->setGeometry(170, 90, 50, 50); + ui->label_graph->setGeometry(210, 70, 81, 81); + ui->label_text->setGeometry(0, 230, 391, 111); + update(); + }); + + QParallelAnimationGroup* group = new QParallelAnimationGroup; + group->addAnimation(animation); + group->addAnimation(animationFrame); + group->start(); +} + +void DavisGUI::setMinStyleWindow(int animDuration) { + m_isMinStyleWindow = true; + hideElementsDuringResize(); + QPropertyAnimation* animationFrame = new QPropertyAnimation(ui->frame_panel, "geometry"); + animationFrame->setDuration(animDuration); + animationFrame->setEasingCurve(QEasingCurve::InOutQuad); + animationFrame->setStartValue(ui->frame_panel->geometry()); + animationFrame->setEndValue(QRect(0, 0, 159, 25)); + QPropertyAnimation* animation = new QPropertyAnimation(this, "geometry"); + animation->setDuration(animDuration); + animation->setEasingCurve(QEasingCurve::InOutQuad); + animation->setStartValue(this->geometry()); + int xOld = this->geometry().x(); + int yOld = this->geometry().y(); + int newWidth = 159; + int newHeight = 137; + int deltaW = newWidth - this->geometry().width(); + animation->setEndValue(QRect(xOld - deltaW, yOld, newWidth, newHeight)); + connect(animation, &QPropertyAnimation::finished, this, [this]() { + ui->label_doc->setVisible(true); + ui->label_arrow->setVisible(true); + ui->label_graph->setVisible(true); + ui->frame_panel->setVisible(true); + ui->label_doc->setGeometry(30, 60, 41, 41); + ui->label_arrow->setGeometry(60, 60, 41, 41); + ui->label_graph->setGeometry(90, 60, 41, 41); + update(); + }); + QParallelAnimationGroup* group = new QParallelAnimationGroup; + group->addAnimation(animation); + group->addAnimation(animationFrame); + group->start(); +} + void DavisGUI::showAboutWindow() { if (isAboutWindowShowed) { delete aboutWindow; @@ -79,14 +252,18 @@ void DavisGUI::showAboutWindow() { isAboutWindowShowed = true; } -void DavisGUI::pasteTextAdded() { +void DavisGUI::pasteFromClipboard() { QClipboard* clipboard = QApplication::clipboard(); QString clipboardText = clipboard->text(); qDebug() << clipboardText; QStringList lines = clipboardText.split(QRegExp("[\r\n]+")); - readPlotText(lines); + if (checkDateTimeVariant(lines) == false) { + readPlotText(lines); + }; } + + void DavisGUI::readPlotText(QStringList& str_lines) { std::vectorlines; std::vector> data; @@ -119,7 +296,7 @@ void DavisGUI::readPlotText(QStringList& str_lines) { } if (data.empty()) { - qDebug() << "Empty file"; + dvs::showReportFileEmpty(); return; } @@ -150,6 +327,160 @@ void DavisGUI::readPlotText(QStringList& str_lines) { } } +bool DavisGUI::checkDateTimeVariant(const QStringList& lines) { + + QJsonArray jarr; + if (jsn::getJsonArrayFromFile("date_time_formats.json", jarr) == false) { + jsn::getJsonArrayFromFile(":/date_time_formats.json", jarr); + } + qDebug() << jarr; + QString dates; + std::vector values; + + for (int i = 0; i < lines.size(); ++i) { + QString test = lines[i]; + for (int j = 0; j < jarr.size(); ++j) { + int template_time_stamp_size = jarr[j].toString().size(); + QString template_time_stamp = jarr[j].toString(); + if (test.size() < template_time_stamp_size + 1) { + continue; + } + QString separator = QString(test[template_time_stamp_size]); + QString substr = test.mid(0, template_time_stamp_size); + QDateTime dt = QDateTime::fromString(substr, template_time_stamp); + if (dt.isValid()) { + //2013-10-04 22:23:00 + qDebug() << dt.toString("yyyy-MM-dd hh:mm:ss"); + dates.append("'"); + dates.append(dt.toString("yyyy-MM-dd hh:mm:ss")); + dates.append("'"); + if (i < lines.size() - 1) { + dates.append(","); + } + + auto values_list = test.split(separator); + if (values_list.size() != 2) { + continue; + } + double value = values_list[1].toDouble(); + qDebug() << value; + values.emplace_back(value); + + } + } + } + if (values.size() == 0) + return false; + qDebug() << "check sizes: " << lines.size() << values.size(); + if (lines.size() != values.size()) { + return false; + } + dvs::showDateTimeChart(dates.toStdString(), values); + return true; + + +} +void DavisGUI::selectAndShowFiles() { + QApplication::processEvents(); + QStringList fileNames = QFileDialog::getOpenFileNames(this, + QObject::tr("Open Files"), + "", + QObject::tr("All Files (*)")); + + visualizeFiles(fileNames); + +} + +bool DavisGUI::isFileContainsSingleChart(const QString& pathToFile, + QString& outX, + QString& outY) { + QFile file(pathToFile); + QTextStream ts(&file); + ts.setCodec("UTF-8"); + if (file.open(QIODevice::ReadWrite) == false) { + return false; + }; + QString line; + QStringList str_lines; + while (ts.readLineInto(&line)) { + str_lines.append(line); + } + if (str_lines.empty()) { + return false; + } + file.close(); + + std::vector> data; + char separator; + for (int i = 0; i < str_lines.size(); ++i) { + std::vectorvalues; + auto res = dvs::find_separator(str_lines[i].toStdString(), separator); + bool is_one_value = false; + std::replace(str_lines[i].begin(), str_lines[i].end(), ',', '.'); + if (res != dvs::GOOD_SEPARATOR) { + if (dvs::is_string_convertable_to_digit(str_lines[i].toStdString()) == false) { + continue; + } else { + is_one_value = true; + } + } + if (is_one_value == false) { + QStringList str_values = str_lines[i].split(separator); + for (int j = 0; j < str_values.size(); ++j) { + if (dvs::is_string_convertable_to_digit(str_values[j].toStdString()) == false) { + continue; + } + values.emplace_back(std::stod(str_values[j].toStdString())); + } + } else { + values.emplace_back(std::stod(str_lines[i].toStdString())); + } + data.emplace_back(values); + } + if (data.empty()) + return false; + + for (size_t i = 0; i < data.size(); ++i) { + if (data[i].size() == 1) { + + double value = data[i][0]; + outX.append(QString::number(i + 1)); + if (i < data.size() - 1) + outX.append(","); + outY.append(QString::number(value)); + if (i < data.size() - 1) + outY.append(","); + + } else if (data[i].size() == 2) { + + outX.append(QString::number(data[i][0])); + if (i < data.size() - 1) + outX.append(","); + outY.append(QString::number(data[i][1])); + if (i < data.size() - 1) + outY.append(","); + + } else { + + + for (size_t j = 0; j < data[i].size(); ++j) { + outX.append(QString::number(j + 1)); + if (j < data[i].size() - 1) + outX.append(","); + outY.append(QString::number(data[i][j])); + if (j < data[i].size() - 1) + outY.append(","); + } + + } + } + qDebug() << outX; + qDebug() << outY; + return true; +} + + + void DavisGUI::dragEnterEvent(QDragEnterEvent* event) { if (event->mimeData()->hasUrls()) { event->acceptProposedAction(); @@ -158,12 +489,48 @@ void DavisGUI::dragEnterEvent(QDragEnterEvent* event) { } } + void DavisGUI::dropEvent(QDropEvent* event) { - QString filePath = event->mimeData()->urls().first().toLocalFile(); + QList file_list_urls = event->mimeData()->urls(); + QStringList file_list; + for (int i = 0; i < file_list_urls.size(); ++i) { + file_list.append(file_list_urls[i].toLocalFile()); + } + visualizeFiles(file_list); +} + +void DavisGUI::visualizeFiles(const QStringList& file_list) { + if (file_list.isEmpty()) { + return; + } + QString all_chart_blocks; + const QString trace_name = "trace%1"; + QString all_traces_names; + if (file_list.size() > 1) { + QStringList onlySingleChartList; + qDebug() << "file list size: " << file_list.size(); + for (int i = 0; i < file_list.size(); ++i) { + QString outX, outY; + QString trace_block = dvs::kHtmlMultiChartBlock; + if (isFileContainsSingleChart(file_list[i], outX, outY)) { + //qDebug()<" << filePath; if (info.exists()) { - qDebug() << "exist"; QFile file(filePath); QTextStream ts(&file); ts.setCodec("UTF-8"); @@ -171,6 +538,17 @@ void DavisGUI::dropEvent(QDropEvent* event) { dvs::showReportFileNotFounded(); return; }; + + QString suffix = info.suffix(); + QStringList suffixes = {"jpg", "bmp", "png", "svg", "mp4", "json"}; + for (int i = 0; i < suffixes.size(); ++i) { + if (suffix == suffixes[i]) { + QProcess process; + process.startDetached("cmd.exe", QStringList() << "/C" << filePath); + return; + } + } + QString line; QStringList str_lines; while (ts.readLineInto(&line)) { @@ -181,7 +559,9 @@ void DavisGUI::dropEvent(QDropEvent* event) { return; } file.close(); - readPlotText(str_lines); + if (checkDateTimeVariant(str_lines) == false) { + readPlotText(str_lines); + }; } else { qDebug() << "not exist"; dvs::showReportFileNotFounded(); @@ -189,17 +569,17 @@ void DavisGUI::dropEvent(QDropEvent* event) { } void DavisGUI::paintEvent(QPaintEvent* event) { - const int PADDING = 20; + const int PADDING = 10; QRectF rectangle(PADDING, PADDING + 20, this->width() - 2 * PADDING, this->height() - 2 * PADDING - 20); QPainter painter(this); painter.setRenderHint(QPainter::Antialiasing); QPainterPath path; - path.addRoundedRect(rectangle, 10, 10); + path.addRoundedRect(rectangle, 5, 5); QPen dashpen; dashpen.setStyle(Qt::DashLine); dashpen.setColor(QColor(150, 150, 150)); - dashpen.setWidth(3); + dashpen.setWidth(2); painter.setPen(dashpen); painter.fillPath(path, QColor(60, 60, 60)); painter.drawPath(path); @@ -215,3 +595,11 @@ void DavisGUI::mouseMoveEvent(QMouseEvent* event) { move(event->globalPos() - m_point); } +void DavisGUI::keyPressEvent(QKeyEvent* event) { + if (event->modifiers() == Qt::ControlModifier && event->key() == Qt::Key_V) { + pasteFromClipboard(); + } else { + QMainWindow::keyPressEvent(event); + } +} + diff --git a/gui/davis_gui.h b/gui/davis_gui.h index 2d636d6..16ab5ff 100644 --- a/gui/davis_gui.h +++ b/gui/davis_gui.h @@ -4,6 +4,7 @@ #include #include "about_window.h" #include "QAction" +#include "animated_button.h" QT_BEGIN_NAMESPACE namespace Ui { class DavisGUI; } @@ -15,10 +16,23 @@ class DavisGUI : public QMainWindow { public: DavisGUI(QWidget* parent = nullptr); ~DavisGUI(); + void show(); + + private: + void setMaxStyleWindow(int animDuration); + void setMinStyleWindow(int animDuration); + void readPlotText(QStringList& str_lines); + void selectAndShowFiles(); + bool checkDateTimeVariant(const QStringList& lines); + bool isFileContainsSingleChart(const QString& pathToFile, + QString& outX, + QString& outY); + void visualizeFiles(const QStringList& file_list); + void hideElementsDuringResize(); private slots: void showAboutWindow(); - void pasteTextAdded(); + void pasteFromClipboard(); private: Ui::DavisGUI* ui; @@ -28,7 +42,10 @@ class DavisGUI : public QMainWindow { About_window* aboutWindow; bool isAboutWindowShowed; QAction* m_copy_paste_action; - void readPlotText(QStringList& str_lines); + bool m_isMinStyleWindow; + AnimatedButton* qpbBuffer; + AnimatedButton* qpbOpen; + // QWidget interface protected: @@ -37,5 +54,6 @@ class DavisGUI : public QMainWindow { void paintEvent(QPaintEvent* event) override; void mousePressEvent(QMouseEvent* event) override; void mouseMoveEvent(QMouseEvent* event) override; + void keyPressEvent(QKeyEvent* event) override; }; #endif // DAVISGUI_H diff --git a/gui/davis_gui.ui b/gui/davis_gui.ui index 34d0752..b4521bb 100644 --- a/gui/davis_gui.ui +++ b/gui/davis_gui.ui @@ -7,7 +7,7 @@ 0 0 397 - 378 + 370 @@ -16,6 +16,11 @@ 0 + + + 11 + + Qt::ActionsContextMenu @@ -26,16 +31,16 @@ Qt::ActionsContextMenu - + false - 40 - 150 - 311 - 211 + 0 + 230 + 391 + 111 @@ -50,30 +55,19 @@ - <html><head/><body><p align="center"><span style=" color:#969696;">drag &amp; drop<br/>text file with numbers <br/>for visualize it's data</span></p><p align="center"><span style=" font-size:12pt; color:#969696;">the file must contain 1 or 2 columns<br/>or be a rectangular matrix in csv-like format</span></p></body></html> - - - - - - 20 - 10 - 361 - 21 - + <html><head/><body><p align="center"><span style=" font-size:20pt; color:#969696;">drag &amp; drop<br/>text file(s) containing<br/>numbers for visualize</span></p></body></html> - - + true - 90 - 60 - 91 - 91 + 110 + 70 + 71 + 61 @@ -86,16 +80,16 @@ true - + true - 210 + 220 70 - 81 - 81 + 71 + 61 @@ -108,16 +102,16 @@ true - + true - 170 - 90 - 50 - 50 + 180 + 80 + 41 + 41 @@ -133,6 +127,42 @@ true + + + + 4 + 4 + 391 + 21 + + + + QFrame::NoFrame + + + QFrame::Raised + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + diff --git a/gui/json_utils.cpp b/gui/json_utils.cpp new file mode 100644 index 0000000..c8bc2df --- /dev/null +++ b/gui/json_utils.cpp @@ -0,0 +1,82 @@ +#include "json_utils.h" + +#include +#include +#include +#include +#include +#include +#include +#include + + +namespace jsn { + +bool getJsonObjectFromFile(const QString& path, + QJsonObject& object) { + QFile file(path); + if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) { + qDebug() << "File can't be opened!" << path; + return false; + }; + QByteArray data = file.readAll(); + QJsonParseError errorPtr; + object = QJsonDocument::fromJson(data, &errorPtr).object(); + if (object.isEmpty()) { + qDebug() << "JSON IS EMPTY: " << errorPtr.errorString(); + return false; + } + file.close(); + + return true; +} + +bool getJsonArrayFromFile(const QString& path, + QJsonArray& object) { + QFile file(path); + if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) { + qDebug() << "File can't be opened!" << path; + return false; + }; + QByteArray data = file.readAll(); + QJsonParseError errorPtr; + object = QJsonDocument::fromJson(data, &errorPtr).array(); + if (object.isEmpty()) { + qDebug() << "JSON IS EMPTY: " << errorPtr.errorString(); + return false; + } + file.close(); + return true; +} + +bool saveJsonObjectToFile(const QString& path, + const QJsonObject& json_object, + QJsonDocument::JsonFormat format) { + QFile file(path); + if (!file.open(QIODevice::WriteOnly)) + return false; + auto json_doc = QJsonDocument(json_object).toJson(format); + auto result = file.write(json_doc); + file.close(); + if (result == -1) + return false; + else + return true; +} + +bool saveJsonArrayToFile(const QString& path, + const QJsonArray& json_object, + QJsonDocument::JsonFormat format) { + QFile file(path); + if (!file.open(QIODevice::WriteOnly)) + return false; + auto json_doc = QJsonDocument(json_object).toJson(format); + auto result = file.write(json_doc); + file.close(); + if (result == -1) + return false; + else + return true; +} + +} // end jsn namespace diff --git a/gui/json_utils.h b/gui/json_utils.h new file mode 100644 index 0000000..64a074c --- /dev/null +++ b/gui/json_utils.h @@ -0,0 +1,32 @@ +#ifndef JSON_UTILS_H +#define JSON_UTILS_H + +#include "QVector" +#include "QJsonDocument" + +class QString; +class QJsonArray; +class QJsonObject; + + +namespace jsn { + + +bool getJsonObjectFromFile(const QString& path, + QJsonObject& object); + +bool getJsonArrayFromFile(const QString& path, + QJsonArray& object); + + +bool saveJsonObjectToFile(const QString& path, + const QJsonObject& json_object, + QJsonDocument::JsonFormat format); + +bool saveJsonArrayToFile(const QString& path, + const QJsonArray& json_object, + QJsonDocument::JsonFormat format); + +} // end namespace jsn + +#endif // JSON_UTILS_H diff --git a/gui/main.cpp b/gui/main.cpp index 1acdf8b..ad1d778 100644 --- a/gui/main.cpp +++ b/gui/main.cpp @@ -32,7 +32,6 @@ void applyDark() { darkPalette.setColor(QPalette::Disabled, QPalette::Highlight, QColor(80, 80, 80)); darkPalette.setColor(QPalette::HighlightedText, gray); darkPalette.setColor(QPalette::Disabled, QPalette::HighlightedText, QColor(127, 127, 127)); - qApp->setPalette(darkPalette); }; diff --git a/gui/res.qrc b/gui/res.qrc index 3a7169f..e618b9a 100644 --- a/gui/res.qrc +++ b/gui/res.qrc @@ -9,5 +9,6 @@ res/davis.mp3 res/content_copy_200dp_969696_FILL0_wght300_GRAD0_opsz48.png res/check_200dp_969696_FILL0_wght300_GRAD0_opsz48.png + date_time_formats.json diff --git a/plotly_maker/html_parts.cpp b/plotly_maker/html_parts.cpp index ed6e2cf..8f67005 100644 --- a/plotly_maker/html_parts.cpp +++ b/plotly_maker/html_parts.cpp @@ -328,6 +328,79 @@ const char kNoFileFoundedPage[] = R"( extern const char kWarningIcon[] = R"davis_delimeter()davis_delimeter"; + + +extern const char kHtmlDateTimeModel[] = R"davis_delimeter( + + + +
+ + + +)davis_delimeter"; + + + +extern const char kHtmlMultiChartBlock[] = R"davis_delimeter( +var trace%1 = { + x: [%2], + y: [%3], + type: 'scatter' +}; +)davis_delimeter"; + + + + +extern const char kHtmlMultiChartModel[] = R"davis_delimeter( + + + +
+ + +)davis_delimeter"; + + // *INDENT-ON* //#STOP_GRAB_TO_DVS_NAMESPACE } // namespace dvs diff --git a/plotly_maker/html_parts.h b/plotly_maker/html_parts.h index 5b44c41..cd2944e 100644 --- a/plotly_maker/html_parts.h +++ b/plotly_maker/html_parts.h @@ -32,6 +32,22 @@ enum ARGS_REPORT_PAGE_INDEX { ARGS_REPORT_PAGE_SIZE }; +enum ARGS_DATE_TIME_PAGE_INDEX { + ARG_JS_NAME, //%1 + ARG_DATE_TIME_VALUES, //%2 + ARG_Y_DATE_TIME_VALUES, //%3 + // ADD NEW ENUM BEFORE THIS COMMENT + ARGS_DATE_TIME_PAGE_SIZE +}; + +enum ARGS_MULTI_CHARTS_PAGE { + ARG_JS_MC_NAME, + ARG_TRACES_BLOCKS, + ARG_DATA_OF_TRACES, + // ADD NEW ENUM BEFORE THIS COMMENT + ARGS_MULTI_CHARTS_PAGE_SIZE +}; + extern const char kHtmlModel[]; extern const char kColorMapDefaultPart[]; @@ -52,6 +68,13 @@ extern const char kWarningJSLibAbsentPage[]; extern const char kNoFileFoundedPage[]; extern const char kWarningIcon[]; + +extern const char kHtmlDateTimeModel[]; + + +extern const char kHtmlMultiChartBlock[]; +extern const char kHtmlMultiChartModel[]; + //#STOP_GRAB_TO_DVS_NAMESPACE } diff --git a/plotly_maker/plotly_maker.cpp b/plotly_maker/plotly_maker.cpp index 4857e55..2cfaecb 100644 --- a/plotly_maker/plotly_maker.cpp +++ b/plotly_maker/plotly_maker.cpp @@ -340,6 +340,37 @@ void showMatrixSizesAreNotTheSame(int badRow) { text); } +void showDateTimeChart(const string& date_time_values, + const vector& yValues) { + + string out; + string davis_dir; +#ifdef _WIN32 + davis_dir = "\\davis_htmls"; +#elif __linux__ + davis_dir = "/davis_htmls"; +#endif + vectorargs {ARGS_DATE_TIME_PAGE_SIZE, ""}; + args[ARG_JS_NAME] = kPlotlyJsName; + args[ARG_DATE_TIME_VALUES] = date_time_values; + + std::string values; + for (size_t i = 0; i < yValues.size(); ++i) { + std::string value = std::to_string(yValues[i]); + values.append(value); + if (i != yValues.size() - 1) { + values.append(","); + } + } + + args[ARG_Y_DATE_TIME_VALUES] = values; + make_string(kHtmlDateTimeModel, args, out); + saveStringToFile(kReportPagePath, out); + openFileBySystem(kReportPagePath); + + +} + //#STOP_GRAB_TO_DVS_NAMESPACE }; // namespace dvs diff --git a/plotly_maker/plotly_maker.h b/plotly_maker/plotly_maker.h index d8d2ad2..7b440a6 100644 --- a/plotly_maker/plotly_maker.h +++ b/plotly_maker/plotly_maker.h @@ -47,6 +47,9 @@ void showReportFileEmpty(); void showMatrixSizesAreNotTheSame(int badRow); +void showDateTimeChart(const string& date_time_values, + const vector& yValues); + //#STOP_GRAB_TO_DVS_NAMESPACE }; // namespace dvs