diff --git a/extensions/apps/traceAnalyzer/CMakeLists.txt b/extensions/apps/traceAnalyzer/CMakeLists.txt index 2576b624..a6bb3d2a 100644 --- a/extensions/apps/traceAnalyzer/CMakeLists.txt +++ b/extensions/apps/traceAnalyzer/CMakeLists.txt @@ -44,7 +44,14 @@ file(GLOB_RECURSE SOURCE_FILES CONFIGURE_DEPENDS *.cpp) file(GLOB_RECURSE HEADER_FILES CONFIGURE_DEPENDS *.h;*.hpp) # Add Python3 Dependency: -find_package(Python3 COMPONENTS Development) +find_package(Python3 COMPONENTS Development Interpreter) + +FetchContent_Declare( + pybind11 + URL https://github.com/pybind/pybind11/archive/refs/tags/v2.10.4.zip +) + +FetchContent_MakeAvailable(pybind11) # Add QWT Dependency: find_library(QWT_LIBRARY NAMES "qwt" "qwt-qt5" PATHS @@ -74,11 +81,10 @@ add_executable(TraceAnalyzer ${SOURCE_FILES} ${HEADER_FILES}) target_include_directories(TraceAnalyzer PRIVATE ${CMAKE_CURRENT_SOURCE_DIR} PRIVATE ${QWT_INCLUDE_DIRS} - PRIVATE ${Python3_INCLUDE_DIRS} ) target_link_libraries(TraceAnalyzer - PRIVATE ${Python3_LIBRARIES} + PRIVATE pybind11::embed PRIVATE ${QWT_LIBRARY} PRIVATE Qt5::Widgets PRIVATE Qt5::Sql @@ -86,5 +92,12 @@ target_link_libraries(TraceAnalyzer PRIVATE DRAMSys::config ) +set(DRAMSYS_TRACEANALYZER_DIR "${CMAKE_CURRENT_SOURCE_DIR}") + +target_compile_definitions(${PROJECT_NAME} + PUBLIC + DRAMSYS_TRACEANALYZER_DIR="${DRAMSYS_TRACEANALYZER_DIR}" +) + build_source_group() diagnostics_print(${PROJECT_NAME}) diff --git a/extensions/apps/traceAnalyzer/README.md b/extensions/apps/traceAnalyzer/README.md index b202c4b5..60df0658 100644 --- a/extensions/apps/traceAnalyzer/README.md +++ b/extensions/apps/traceAnalyzer/README.md @@ -1,2 +1,13 @@ -dram.vp.scheduler -================= +## TraceAnalyzer +### Python Dependencies +The used Python dependencies of this project include: + - matplotlib + - numpy + - pyvcd + - tqdm + +To install all required packages, a `requirements.txt` file is provided, which can be found in `scripts/requirements.txt`. +Install the packages using this command: +``` +python3 -m pip install -r scripts/requirements.txt +``` diff --git a/extensions/apps/traceAnalyzer/businessObjects/calculatedMetric.h b/extensions/apps/traceAnalyzer/businessObjects/calculatedMetric.h index f1478354..aaec951d 100644 --- a/extensions/apps/traceAnalyzer/businessObjects/calculatedMetric.h +++ b/extensions/apps/traceAnalyzer/businessObjects/calculatedMetric.h @@ -33,30 +33,17 @@ * Janik Schlemminger * Robert Gernhardt * Matthias Jung + * Derek Christ */ #ifndef METRIC_H #define METRIC_H #include -class CalculatedMetric +struct CalculatedMetric { -public: - CalculatedMetric(QString name, double value): name(name), value(value) {} - QString getName() - { - return name; - } - double getValue() - { - return value; - } - -private: - QString name; + std::string name; double value; }; - - #endif // METRIC_H diff --git a/extensions/apps/traceAnalyzer/businessObjects/pythoncaller.cpp b/extensions/apps/traceAnalyzer/businessObjects/pythoncaller.cpp index 29941fd3..ea101240 100644 --- a/extensions/apps/traceAnalyzer/businessObjects/pythoncaller.cpp +++ b/extensions/apps/traceAnalyzer/businessObjects/pythoncaller.cpp @@ -41,205 +41,78 @@ #include #include #include -#include -#include -#include +#include +#include -PythonCaller::PythonCaller() : - metricModuleName("metrics"), - metricFunctionName("calculateMetrics"), - getMetricFunctionName("getMetrics"), - - pathToScripts(QApplication::applicationDirPath().toStdString() + - "/../../extensions/apps/traceAnalyzer/scripts/"), - plotsModuleName("plots"), - plotsFunctionName("generatePlots"), - checkDependenciesModuleName("checkDependencies"), - vcdExportModuleName("vcdExport"), - vcdExportFunctionName("dumpVcd"), - vcdExportDependenciesFunctionName("checkVcdExport") +std::string PythonCaller::generatePlots(std::string_view pathToTrace) { - Py_Initialize(); - PyObject *sysPath = PySys_GetObject((char *)"path"); - PyObject *path = PyUnicode_FromString(this->pathToScripts.c_str()); - PyList_Insert(sysPath, 0, path); - Py_DECREF(path); - - qDebug() << "Metric:" << metricModuleName.c_str() << metricFunctionName.c_str(); - qDebug() << "Plot:" << plotsModuleName.c_str() << plotsFunctionName.c_str(); - qDebug() << "VcdExport:" << vcdExportModuleName.c_str() << vcdExportFunctionName.c_str(); - qDebug() << "Script: " << pathToScripts.c_str(); - - pCalculateMetricsFunction = loadFunctionFromModule(metricModuleName, - metricFunctionName); - pGenPlotsFunction = loadFunctionFromModule(plotsModuleName, plotsFunctionName); - pGetMetricsFunction = loadFunctionFromModule(metricModuleName, - getMetricFunctionName); - - pVcdExportDependenciesFunction = loadFunctionFromModule(checkDependenciesModuleName, vcdExportDependenciesFunctionName); - - if (vcdExportDependenciesAvailable()) - pVcdExportFunction = loadFunctionFromModule(vcdExportModuleName, vcdExportFunctionName); - else - std::cerr << "Warning: Python module pyvcd or tqdm not installed! Exporting as VCD not possible." << std::endl; -} - - -//returns new reference to the function (see: http://edcjones.tripod.com/refcount.html for the difference between "new reference" and "borrowed reference") -PyObject *PythonCaller::loadFunctionFromModule(std::string moduleName, std::string functionName) -{ - PyObject *pModuleName = PyUnicode_FromString(moduleName.c_str()); - PyObject *pModule = PyImport_Import(pModuleName); - - if (!pModule) { - throw std::runtime_error(std::string("Could not load module " + moduleName)); + try + { + pybind11::module_ metricsModule = pybind11::module_::import("plots"); + auto result = metricsModule.attr("generatePlots")(pathToTrace).cast(); + return result; + } + catch (std::exception const &err) + { + std::cout << err.what() << std::endl; } - PyObject *pFunction = PyObject_GetAttrString(pModule, functionName.c_str()); + return {}; +} - if (!pFunction || !PyCallable_Check(pFunction)) { - throw std::runtime_error( - std::string("Could not load test function " + functionName + "in module " + moduleName)); +std::vector PythonCaller::availableMetrics(std::string_view pathToTrace) +{ + try + { + pybind11::module_ metricsModule = pybind11::module_::import("metrics"); + pybind11::list result = metricsModule.attr("getMetrics")(pathToTrace); + return result.cast>(); + } + catch (std::exception const &err) + { + std::cout << err.what() << std::endl; } - Py_DECREF(pModuleName); - Py_DECREF(pModule); - return pFunction; + return {}; } -PythonCaller::~PythonCaller() +TraceCalculatedMetrics PythonCaller::evaluateMetrics(std::string_view pathToTrace, std::vector selectedMetrics) { - Py_DECREF(pCalculateMetricsFunction); - Py_DECREF(pGenPlotsFunction); - Py_DECREF(pGetMetricsFunction); + TraceCalculatedMetrics metrics(pathToTrace.data()); - if (pVcdExportFunction) - Py_DECREF(pVcdExportFunction); + try + { + pybind11::module_ metricsModule = pybind11::module_::import("metrics"); + pybind11::list result = metricsModule.attr("calculateMetrics")(pathToTrace, selectedMetrics); + auto metricList = result.cast>(); - Py_DECREF(pVcdExportDependenciesFunction); - Py_Finalize(); -} - -PyObject *PythonCaller::callMetricsFunction(PyObject *function, QString argument, std::vector list) -{ - assert(PyCallable_Check(function)); - - PyObject *pArgs = PyTuple_New(2); - PyObject *pArgumentString = PyUnicode_FromString( - argument.toStdString().c_str()); - PyObject *pArgumentList = PyList_New(list.size()); - for (size_t i = 0; i < list.size(); i++) { - PyList_SetItem(pArgumentList, i, PyBool_FromLong(list[i])); + for (auto metricPair : metricList) + { + std::string name = metricPair[0].cast(); + double value = metricPair[1].cast(); + metrics.addCalculatedMetric({name, value}); + } } - PyTuple_SetItem(pArgs, 0, pArgumentString); - PyTuple_SetItem(pArgs, 1, pArgumentList); - PyObject *pResult = PyObject_CallObject(function, pArgs); - Py_DECREF(pArgs); - - if (!pResult) { - PyErr_Print(); - throw std::runtime_error( - std::string("Error in calling " + metricFunctionName + " in module " + metricModuleName)); + catch (std::exception const &err) + { + std::cout << err.what() << std::endl; } - return pResult; + return metrics; } -//returns a new reference to result of function call -PyObject *PythonCaller::callFunctionWithStringArgument(PyObject *function, - QString argument) +std::string PythonCaller::dumpVcd(std::string_view pathToTrace) { - assert(PyCallable_Check(function)); - - PyObject *pArgs = PyTuple_New(1); - PyObject *pArgument = PyUnicode_FromString(argument.toStdString().c_str()); - PyTuple_SetItem(pArgs, 0, pArgument); - PyObject *pResult = PyObject_CallObject(function, pArgs); - Py_DECREF(pArgument); - - if (!pResult) { - PyErr_Print(); - throw std::runtime_error(std::string("Error in calling function with string argument")); + try + { + pybind11::module_ vcdModule = pybind11::module_::import("vcdExport"); + pybind11::str result = vcdModule.attr("dumpVcd")(pathToTrace); + return result.cast(); + } + catch (std::exception const &err) + { + std::cout << err.what() << std::endl; } - return pResult; -} - -PyObject *PythonCaller::callFunctionWithoutArguments(PyObject *function) -{ - assert(PyCallable_Check(function)); - PyObject *pResult = PyObject_CallObject(function, NULL); - - if (!pResult) { - PyErr_Print(); - throw std::runtime_error(std::string("Error in calling python function")); - } - - return pResult; -} - -TraceCalculatedMetrics PythonCaller::calculateMetricsOnTrace(QString pathToTrace, std::vector list) -{ - TraceCalculatedMetrics result(QFileInfo(pathToTrace).baseName()); - PyObject *pResult = callMetricsFunction(pCalculateMetricsFunction, pathToTrace, - list); - - for (Py_ssize_t i = 0; i < PyList_Size(pResult); ++i) { - PyObject *calculatedMetric = PyList_GetItem(pResult, i); - QString metricName(PyUnicode_AsUTF8(PyTuple_GetItem(calculatedMetric, 0))); - double value = PyFloat_AsDouble(PyTuple_GetItem(calculatedMetric, 1)); - result.addCalculatedMetric(CalculatedMetric(metricName, value)); - } - - Py_DECREF(pResult); - return result; -} - -std::vector PythonCaller::getMetrics(QString pathToTrace) -{ - std::vector result; - PyObject *pResult = callFunctionWithStringArgument(pGetMetricsFunction, - pathToTrace); - - for (Py_ssize_t i = 0; i < PyList_Size(pResult); ++i) { - PyObject *metric = PyList_GetItem(pResult, i); - QString metricName(PyUnicode_AsUTF8(metric)); - result.push_back(metricName.toStdString().c_str()); - } - - Py_DECREF(pResult); - return result; -} - -QString PythonCaller::generatePlotsOnTrace(QString pathToTrace) -{ - assert(PyCallable_Check(pGenPlotsFunction)); - - PyObject *pResult = callFunctionWithStringArgument(pGenPlotsFunction, - pathToTrace); - QString outputFiles (PyUnicode_AsUTF8(pResult)); - Py_DECREF(pResult); - return outputFiles; - -} - -QString PythonCaller::exportAsVcd(QString pathToTrace) -{ - if (!pVcdExportFunction) - return QString(); - - PyObject *pResult = callFunctionWithStringArgument(pVcdExportFunction, pathToTrace); - - QString dump(PyUnicode_AsUTF8(pResult)); - Py_DECREF(pResult); - return dump; -} - -bool PythonCaller::vcdExportDependenciesAvailable() -{ - PyObject *result = callFunctionWithoutArguments(pVcdExportDependenciesFunction); - bool available = PyObject_IsTrue(result); - Py_DECREF(result); - - return available; + return {}; } diff --git a/extensions/apps/traceAnalyzer/businessObjects/pythoncaller.h b/extensions/apps/traceAnalyzer/businessObjects/pythoncaller.h index 30bd8d8a..6922e3fd 100644 --- a/extensions/apps/traceAnalyzer/businessObjects/pythoncaller.h +++ b/extensions/apps/traceAnalyzer/businessObjects/pythoncaller.h @@ -45,47 +45,16 @@ #undef slots #endif -#include -#include #include -#include #include "businessObjects/tracecalculatedmetrics.h" class PythonCaller { public: - PythonCaller(); - ~PythonCaller(); - - TraceCalculatedMetrics calculateMetricsOnTrace(QString pathToTrace, - std::vector list); - std::vector getMetrics(QString pathToTrace); - QString generatePlotsOnTrace(QString pathToTrace); - - bool vcdExportDependenciesAvailable(); - QString exportAsVcd(QString pathToTrace); - -private: - PyObject *pCalculateMetricsFunction, *pGetMetricsFunction; - PyObject *pGenPlotsFunction; - PyObject *pVcdExportFunction = nullptr; - PyObject *pVcdExportDependenciesFunction; - PyObject *loadFunctionFromModule(std::string moduleName, - std::string functionName); - std::string metricModuleName, metricFunctionName, getMetricFunctionName, pathToScripts; - std::string plotsModuleName; - std::string plotsFunctionName; - - std::string checkDependenciesModuleName; - - std::string vcdExportModuleName; - std::string vcdExportFunctionName; - std::string vcdExportDependenciesFunctionName; - - PyObject *callFunctionWithStringArgument(PyObject *function, QString argument); - PyObject *callFunctionWithoutArguments(PyObject *function); - PyObject *callMetricsFunction(PyObject *function, QString argument, - std::vector list); + static std::vector availableMetrics(std::string_view pathToTrace); + static TraceCalculatedMetrics evaluateMetrics(std::string_view pathToTrace, std::vector selectedMetrics); + static std::string generatePlots(std::string_view pathToTrace); + static std::string dumpVcd(std::string_view pathToTrace); }; #endif // PYTHONCALLER_H diff --git a/extensions/apps/traceAnalyzer/businessObjects/tracecalculatedmetrics.h b/extensions/apps/traceAnalyzer/businessObjects/tracecalculatedmetrics.h index 7c67916a..4594be1d 100644 --- a/extensions/apps/traceAnalyzer/businessObjects/tracecalculatedmetrics.h +++ b/extensions/apps/traceAnalyzer/businessObjects/tracecalculatedmetrics.h @@ -65,7 +65,7 @@ public: result.append("Trace"); for (CalculatedMetric calculatedMetric : calculatedMetrics) { result.append(","); - result.append(calculatedMetric.getName()); + result.append(calculatedMetric.name.c_str()); } return result; } @@ -76,7 +76,7 @@ public: result.append(traceName); for (CalculatedMetric calculatedMetric : calculatedMetrics) { result.append(","); - result.append(QString::number(calculatedMetric.getValue())); + result.append(QString::number(calculatedMetric.value)); } return result; } diff --git a/extensions/apps/traceAnalyzer/evaluationtool.cpp b/extensions/apps/traceAnalyzer/evaluationtool.cpp index 078ea775..07daf82c 100644 --- a/extensions/apps/traceAnalyzer/evaluationtool.cpp +++ b/extensions/apps/traceAnalyzer/evaluationtool.cpp @@ -51,11 +51,9 @@ #include "evaluationtool.h" #include "ui_evaluationtool.h" -using namespace std; - EvaluationTool::EvaluationTool(PythonCaller &pythonCaller, QWidget *parent) : QWidget(parent), - ui(new Ui::EvaluationTool), resourcesRelPath("/../../dram/resources/scripts"""), pythonCaller(pythonCaller) + ui(new Ui::EvaluationTool), pythonCaller(pythonCaller) { ui->setupUi(this); traceFilesModel = new QStandardItemModel(this); @@ -85,16 +83,15 @@ void EvaluationTool::showAndEvaluateMetrics(QList paths) show(); ui->toolBox->setCurrentIndex(1); selectMetrics->setMetrics(getMetrics()); - cout << "done" << endl; } -vector EvaluationTool::getMetrics() +std::vector EvaluationTool::getMetrics() { - vector metrics; + std::vector metrics; for (int row = 0; row < traceFilesModel->rowCount(); ++row) { TraceFileItem *item = static_cast(traceFilesModel->item(row)); - vector result = pythonCaller.getMetrics(item->getPath()); - if (result.size() > metrics.size()) + std::vector result = PythonCaller::availableMetrics(item->getPath().toStdString()); + if (result.size() > metrics.size()) // TODO use std::set metrics = result; } return metrics; @@ -127,22 +124,21 @@ void EvaluationTool::on_btn_calculateMetrics_clicked() void EvaluationTool::getSelectedMetrics() { - vector selectedMetrics; + std::vector selectedMetrics; for (QCheckBox *metric : selectMetrics->metrics) { selectedMetrics.push_back(metric->isChecked()); } calculateMetrics(selectedMetrics); } -void EvaluationTool::calculateMetrics(vector selectedMetrics) +void EvaluationTool::calculateMetrics(std::vector selectedMetrics) { ui->traceMetricTreeWidget->clear(); for (int row = 0; row < traceFilesModel->rowCount(); ++row) { TraceFileItem *item = static_cast(traceFilesModel->item(row)); if (item->checkState() == Qt::Checked) { - TraceCalculatedMetrics result = pythonCaller.calculateMetricsOnTrace( - item->getPath(), selectedMetrics); + TraceCalculatedMetrics result = pythonCaller.evaluateMetrics(item->getPath().toStdString(), selectedMetrics); calculatedMetrics.push_back(result); ui->traceMetricTreeWidget->addTraceMetricResults(result); } @@ -194,9 +190,9 @@ void EvaluationTool::genPlots() TraceFileItem *item = static_cast(traceFilesModel->item(row)); if (item->checkState() == Qt::Checked) { - ui->traceMetricTreeWidget->addTracePlotResults(QFileInfo( - item->getPath()).baseName(), - pythonCaller.generatePlotsOnTrace(item->getPath())); + ui->traceMetricTreeWidget->addTracePlotResults( + QFileInfo(item->getPath()).baseName(), + PythonCaller::generatePlots(item->getPath().toStdString()).c_str()); } } ui->traceMetricTreeWidget->expandAll(); diff --git a/extensions/apps/traceAnalyzer/evaluationtool.h b/extensions/apps/traceAnalyzer/evaluationtool.h index 6c32d487..03ca68db 100644 --- a/extensions/apps/traceAnalyzer/evaluationtool.h +++ b/extensions/apps/traceAnalyzer/evaluationtool.h @@ -84,7 +84,6 @@ private: Ui::EvaluationTool *ui; QStandardItemModel *traceFilesModel; std::vector calculatedMetrics; - QString resourcesRelPath; SelectMetrics *selectMetrics; PythonCaller &pythonCaller; diff --git a/extensions/apps/traceAnalyzer/main.cpp b/extensions/apps/traceAnalyzer/main.cpp index bd9cb900..b807bc03 100644 --- a/extensions/apps/traceAnalyzer/main.cpp +++ b/extensions/apps/traceAnalyzer/main.cpp @@ -42,7 +42,9 @@ #include #include #include +#include #include +#include int main(int argc, char *argv[]) { @@ -54,6 +56,16 @@ int main(int argc, char *argv[]) a.setApplicationName(QStringLiteral("TraceAnalyzer")); a.setApplicationDisplayName(QStringLiteral("Trace Analyzer")); + std::filesystem::path traceAnalyzerDir = DRAMSYS_TRACEANALYZER_DIR; + std::filesystem::path modulesDir = traceAnalyzerDir / "scripts"; + + pybind11::scoped_interpreter guard; + + // Add scripts directory to local module search path + pybind11::module_ sys = pybind11::module_::import("sys"); + pybind11::list path = sys.attr("path"); + path.append(modulesDir.c_str()); + if (argc > 1) { QSet arguments; for (int i = 1; i < argc; ++i) diff --git a/extensions/apps/traceAnalyzer/presentation/tracemetrictreewidget.cpp b/extensions/apps/traceAnalyzer/presentation/tracemetrictreewidget.cpp index 93aad60a..0ca4c737 100644 --- a/extensions/apps/traceAnalyzer/presentation/tracemetrictreewidget.cpp +++ b/extensions/apps/traceAnalyzer/presentation/tracemetrictreewidget.cpp @@ -55,7 +55,7 @@ void TraceMetricTreeWidget::addTraceMetricResults(const TraceCalculatedMetrics new QTreeWidgetItem(top, {QString("Number of threads: 1")}); } else { for (CalculatedMetric calculatedMetric : result.getCalculatedMetrics()) { - new QTreeWidgetItem(top, {calculatedMetric.getName() + QString(": ") + QString::number(calculatedMetric.getValue(), 'f')}); + new QTreeWidgetItem(top, {calculatedMetric.name.c_str() + QString(": ") + QString::number(calculatedMetric.value, 'f')}); } } } diff --git a/extensions/apps/traceAnalyzer/scripts/requirements.txt b/extensions/apps/traceAnalyzer/scripts/requirements.txt new file mode 100644 index 00000000..2d51111b --- /dev/null +++ b/extensions/apps/traceAnalyzer/scripts/requirements.txt @@ -0,0 +1,4 @@ +matplotlib +numpy +pyvcd +tqdm diff --git a/extensions/apps/traceAnalyzer/traceanalyzer.cpp b/extensions/apps/traceAnalyzer/traceanalyzer.cpp index 499ea212..4ed91111 100644 --- a/extensions/apps/traceAnalyzer/traceanalyzer.cpp +++ b/extensions/apps/traceAnalyzer/traceanalyzer.cpp @@ -127,13 +127,12 @@ void TraceAnalyzer::on_menuFile_aboutToShow() ui->actionQuit->setEnabled(true); bool tabsOpen = ui->traceFileTabs->count() > 0; - bool exportAsVcdAvailable = pythonCaller.vcdExportDependenciesAvailable(); ui->actionSave->setEnabled(tabsOpen); ui->actionSave_all->setEnabled(tabsOpen); ui->actionReload->setEnabled(tabsOpen); ui->actionReload_all->setEnabled(tabsOpen); - ui->actionExportAsVCD->setEnabled(tabsOpen && exportAsVcdAvailable); + ui->actionExportAsVCD->setEnabled(tabsOpen); ui->actionTest->setEnabled(tabsOpen); ui->actionMetrics->setEnabled(tabsOpen); ui->actionClose->setEnabled(tabsOpen); diff --git a/extensions/apps/traceAnalyzer/tracefiletab.cpp b/extensions/apps/traceAnalyzer/tracefiletab.cpp index d5d0feb9..8657d5d6 100644 --- a/extensions/apps/traceAnalyzer/tracefiletab.cpp +++ b/extensions/apps/traceAnalyzer/tracefiletab.cpp @@ -41,7 +41,6 @@ #include "businessObjects/commentmodel.h" #include "businessObjects/configmodels.h" #include "businessObjects/dramTimeDependencies/phasedependenciestracker.h" -#include "businessObjects/pythoncaller.h" #include "businessObjects/traceplotlinemodel.h" #include "businessObjects/tracetime.h" #include "queryeditor.h" @@ -68,6 +67,9 @@ #include #include +#include +#include + TraceFileTab::TraceFileTab(std::string_view traceFilePath, PythonCaller &pythonCaller, QWidget *parent) : QWidget(parent), ui(new Ui::TraceFileTab), commentModel(new CommentModel(this)), navigator(new TraceNavigator(traceFilePath.data(), commentModel, this)), @@ -117,23 +119,14 @@ void TraceFileTab::commitChangesToDB() void TraceFileTab::exportAsVCD() { - QString filename = QFileDialog::getSaveFileName(this, "Export to VCD", "", - "VCD files (*.vcd)"); - auto dumpVcd = [=]() { - QString dump = pythonCaller.exportAsVcd(traceFilePath.data()); + std::string filename = QFileDialog::getSaveFileName(this, "Export to VCD", "", "VCD files (*.vcd)").toStdString(); - QFile file(filename); - file.open(QIODevice::WriteOnly | QIODevice::Text); - QTextStream out(&file); - out << dump; - file.close(); + auto dump = PythonCaller::dumpVcd(traceFilePath); + + std::ofstream file(filename); + file << dump; - Q_EMIT statusChanged(QString("VCD export finished.")); - }; - - if (filename != "") { - QtConcurrent::run(dumpVcd); - } + Q_EMIT statusChanged(QString("VCD export finished.")); } void TraceFileTab::setUpTraceSelector()