Merge branch 'work/traceanalyzer_comments' into 'develop'

Rewrite of comment system / add functionality to select comments and directly delete/edit them

See merge request ems/astdm/dram.sys!308
This commit is contained in:
Lukas Steiner
2021-09-21 08:12:17 +00:00
21 changed files with 757 additions and 485 deletions

View File

@@ -75,7 +75,6 @@ add_executable(TraceAnalyzer
presentation/tracescroller.cpp
traceanalyzer.cpp
presentation/transactiontreewidget.cpp
presentation/commenttreewidget.cpp
presentation/util/clkgrid.cpp
queryeditor.cpp
presentation/selectedtransactiontreewidget.cpp
@@ -97,6 +96,7 @@ add_executable(TraceAnalyzer
presentation/util/customlabelscaledraw.cpp
presentation/traceselector.cpp
businessObjects/configmodels.cpp
businessObjects/commentmodel.cpp
selectmetrics.ui
preferences.ui

View File

@@ -1,64 +0,0 @@
/*
* Copyright (c) 2015, Technische Universität Kaiserslautern
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* 3. Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER
* OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* Authors:
* Janik Schlemminger
* Robert Gernhardt
* Matthias Jung
* Derek Christ
*/
#ifndef COMMENT_H
#define COMMENT_H
#include <QString>
#include "timespan.h"
class Comment
{
traceTime time;
QString text;
public:
Comment(traceTime time, QString text): time(time), text(text) {}
traceTime Time() const
{
return time;
}
const QString &Text() const
{
return text;
}
void changeText(QString newText)
{
text = newText;
}
};
#endif // COMMENT_H

View File

@@ -0,0 +1,305 @@
/*
* Copyright (c) 2021, Technische Universität Kaiserslautern
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* 3. Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER
* OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* Authors:
* Derek Christ
*/
#include "commentmodel.h"
#include <QMenu>
#include <QItemSelectionModel>
#include <QKeyEvent>
CommentModel::CommentModel(QObject *parent) : QAbstractTableModel(parent),
gotoAction(new QAction("Goto comment", this)),
editAction(new QAction("Edit comment", this)),
deleteAction(new QAction("Delete comment", this)),
selectAllAction(new QAction("Select all comments", this)),
deselectAllAction(new QAction("Deselect all comments", this)),
internalSelectionModel(new QItemSelectionModel(this, this))
{
setUpActions();
}
void CommentModel::setUpActions()
{
QObject::connect(gotoAction, &QAction::triggered, this, [=](){
const QModelIndexList indexes = internalSelectionModel->selectedRows();
for (const QModelIndex &currentIndex : indexes)
{
emit gotoCommentTriggered(currentIndex);
}
});
QObject::connect(editAction, &QAction::triggered, this, [=](){
const QModelIndexList indexes = internalSelectionModel->selectedRows();
for (const QModelIndex &currentIndex : indexes)
emit editTriggered(index(currentIndex.row(), static_cast<int>(Column::Comment)));
});
QObject::connect(deleteAction, &QAction::triggered, this, [=](){
const QModelIndexList indexes = internalSelectionModel->selectedRows();
for (const QModelIndex &currentIndex : indexes)
removeComment(currentIndex);
});
QObject::connect(selectAllAction, &QAction::triggered, this, [=](){
QModelIndex topLeft = index(0, 0);
QModelIndex bottomRight = index(rowCount() - 1, columnCount() - 1);
internalSelectionModel->select(QItemSelection(topLeft, bottomRight),
QItemSelectionModel::Select | QItemSelectionModel::Rows);
});
QObject::connect(deselectAllAction, &QAction::triggered,
internalSelectionModel, &QItemSelectionModel::clearSelection);
}
int CommentModel::rowCount(const QModelIndex &parent) const
{
Q_UNUSED(parent)
return comments.size();
}
int CommentModel::columnCount(const QModelIndex &parent) const
{
Q_UNUSED(parent)
return static_cast<int>(Column::COLUMNCOUNT);
}
QVariant CommentModel::data(const QModelIndex &index, int role) const
{
if (!index.isValid())
return QVariant();
// Qt::UserRole is used to get the raw time without pretty formatting.
if (role != Qt::DisplayRole && role != Qt::EditRole && role != Qt::UserRole)
return QVariant();
const Comment &comment = comments.at(index.row());
if (role == Qt::UserRole && static_cast<Column>(index.column()) == Column::Time)
return QVariant(comment.time);
switch (static_cast<Column>(index.column())) {
case Column::Time:
return QVariant(prettyFormatTime(comment.time));
case Column::Comment:
return QVariant(comment.text);
default:
break;
}
return QVariant();
}
bool CommentModel::setData(const QModelIndex &index, const QVariant &value, int role)
{
if (!index.isValid())
return false;
if (role != Qt::EditRole)
return false;
if (static_cast<Column>(index.column()) != Column::Comment)
return false;
QString newText = value.toString();
Comment &comment = comments.at(index.row());
comment.text = newText;
emit dataChanged(index, index);
return true;
}
QVariant CommentModel::headerData(int section, Qt::Orientation orientation, int role) const
{
if (role != Qt::DisplayRole)
return QVariant();
if (orientation == Qt::Horizontal)
{
switch (static_cast<Column>(section)) {
case Column::Time:
return "Time";
case Column::Comment:
return "Comment";
default:
break;
}
}
return QVariant();
}
Qt::ItemFlags CommentModel::flags(const QModelIndex &index) const
{
Qt::ItemFlags flags = QAbstractItemModel::flags(index);
if (index.isValid() && index.column() == static_cast<int>(Column::Comment))
flags |= Qt::ItemIsEditable;
return flags;
}
void CommentModel::openContextMenu()
{
if (!internalSelectionModel->hasSelection())
return;
QMenu *menu = new QMenu();
menu->addActions({gotoAction, editAction, deleteAction});
menu->addSeparator();
menu->addActions({selectAllAction, deselectAllAction});
QObject::connect(menu, &QMenu::aboutToHide, [=]() {
menu->deleteLater();
});
menu->popup(QCursor::pos());
}
QItemSelectionModel *CommentModel::selectionModel() const
{
return internalSelectionModel;
}
void CommentModel::addComment(traceTime time)
{
auto commentIt = std::find_if(comments.rbegin(), comments.rend(), [time](const Comment &comment){
return comment.time <= time;
});
int insertIndex = std::distance(comments.begin(), commentIt.base());
beginInsertRows(QModelIndex(), insertIndex, insertIndex);
comments.insert(comments.begin() + insertIndex, {time, "Enter comment text..."});
endInsertRows();
internalSelectionModel->setCurrentIndex(index(insertIndex, 0),
QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows);
emit editTriggered(index(insertIndex, 1));
}
void CommentModel::addComment(traceTime time, QString text)
{
auto commentIt = std::find_if(comments.rbegin(), comments.rend(), [time](const Comment &comment){
return comment.time <= time;
});
int insertIndex = std::distance(comments.begin(), commentIt.base());
beginInsertRows(QModelIndex(), insertIndex, insertIndex);
comments.insert(comments.begin() + insertIndex, {time, text});
endInsertRows();
}
void CommentModel::removeComment(traceTime time)
{
auto commentIt = std::find_if(comments.begin(), comments.end(), [time](const Comment &comment){
return comment.time == time;
});
if (commentIt == comments.end())
return;
int removeIndex = std::distance(comments.begin(), commentIt);
beginRemoveRows(QModelIndex(), removeIndex, removeIndex);
comments.erase(commentIt);
endRemoveRows();
}
void CommentModel::removeComment(const QModelIndex &index)
{
beginRemoveRows(QModelIndex(), index.row(), index.row());
comments.erase(comments.begin() + index.row());
endRemoveRows();
}
const std::vector<CommentModel::Comment> &CommentModel::getComments() const
{
return comments;
}
traceTime CommentModel::getTimeFromIndex(const QModelIndex &index) const
{
Q_ASSERT(comments.size() > index.row());
return comments[index.row()].time;
}
QModelIndex CommentModel::hoveredComment(Timespan timespan) const
{
auto commentIt = std::find_if(comments.begin(), comments.end(), [timespan](const Comment &comment){
return timespan.Begin() < comment.time && comment.time < timespan.End();
});
if (commentIt == comments.end())
return QModelIndex();
int commentIndex = std::distance(comments.begin(), commentIt);
return index(commentIndex, 0);
}
bool CommentModel::eventFilter(QObject *object, QEvent *event)
{
Q_UNUSED(object)
if (event->type() == QEvent::KeyPress)
{
QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event);
if (keyEvent->key() == Qt::Key_Delete)
{
const QModelIndexList indexes = internalSelectionModel->selectedRows();
for (const QModelIndex &currentIndex : indexes)
{
removeComment(currentIndex);
}
}
}
return false;
}
void CommentModel::rowDoubleClicked(const QModelIndex &index)
{
if (!index.isValid())
return;
if (static_cast<Column>(index.column()) == Column::Time)
Q_EMIT gotoCommentTriggered(index);
}

View File

@@ -0,0 +1,119 @@
/*
* Copyright (c) 2021, Technische Universität Kaiserslautern
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* 3. Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER
* OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* Authors:
* Derek Christ
*/
#ifndef COMMENTMODEL_H
#define COMMENTMODEL_H
#include "tracetime.h"
#include "timespan.h"
#include <QAbstractTableModel>
#include <utility>
class QAction;
class QItemSelectionModel;
class CommentModel : public QAbstractTableModel
{
Q_OBJECT
public:
explicit CommentModel(QObject *parent = nullptr);
struct Comment {
traceTime time;
QString text;
};
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
int columnCount(const QModelIndex &parent = QModelIndex()) const override;
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override;
Qt::ItemFlags flags(const QModelIndex &index) const override;
enum class Column {
Time = 0,
Comment,
COLUMNCOUNT
};
void openContextMenu();
QItemSelectionModel *selectionModel() const;
void addComment(traceTime time);
void addComment(traceTime time, QString text);
void removeComment(traceTime time);
void removeComment(const QModelIndex &index);
const std::vector<Comment> &getComments() const;
traceTime getTimeFromIndex(const QModelIndex &index) const;
QModelIndex hoveredComment(Timespan timespan) const;
public Q_SLOTS:
void rowDoubleClicked(const QModelIndex &index);
protected:
bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) override;
/**
* The eventFilter is used to delete comments using the delete key.
*/
bool eventFilter(QObject *object, QEvent *event) override;
Q_SIGNALS:
void editTriggered(const QModelIndex &index);
void gotoCommentTriggered(const QModelIndex &index);
private:
void setUpActions();
std::vector<Comment> comments;
QAction *gotoAction;
QAction *editAction;
QAction *deleteAction;
QAction *selectAllAction;
QAction *deselectAllAction;
QItemSelectionModel *internalSelectionModel;
};
#endif // COMMENTMODEL_H

View File

@@ -44,7 +44,6 @@
#include <QFileInfo>
#include <QDebug>
#include "data/tracedb.h"
#include "businessObjects/comment.h"
#include "businessObjects/phases/phasefactory.h"
@@ -87,7 +86,7 @@ void TraceDB::prepareQueries()
selectDebugMessagesByTimespanWithLimit.prepare("SELECT time, Message FROM DebugMessages WHERE :begin <= time AND time <= :end LIMIT :limit");
}
void TraceDB::updateComments(vector<Comment> comments)
void TraceDB::updateComments(const std::vector<CommentModel::Comment> &comments)
{
QSqlQuery query(database);
@@ -95,9 +94,10 @@ void TraceDB::updateComments(vector<Comment> comments)
executeQuery(query);
query.prepare("insert into Comments values(:time,:text)");
for (const Comment &comment : comments) {
query.bindValue(":time", comment.Time());
query.bindValue(":text", comment.Text());
for (const auto &comment : comments)
{
query.bindValue(":time", comment.time);
query.bindValue(":text", comment.text);
executeQuery(query);
}
}
@@ -341,7 +341,7 @@ unsigned int TraceDB::getNumberOfPhases()
return query.value(0).toInt();
}
vector<Comment> TraceDB::getComments()
vector<CommentModel::Comment> TraceDB::getComments()
{
QSqlQuery query(database);
query.prepare("SELECT Time,Text From Comments");
@@ -350,7 +350,7 @@ vector<Comment> TraceDB::getComments()
}
vector<Comment> TraceDB::getDebugMessagesInTimespan(const Timespan &span)
vector<CommentModel::Comment> TraceDB::getDebugMessagesInTimespan(const Timespan &span)
{
selectDebugMessagesByTimespan.bindValue(":begin", span.Begin());
selectDebugMessagesByTimespan.bindValue(":end", span.End());
@@ -359,7 +359,7 @@ vector<Comment> TraceDB::getDebugMessagesInTimespan(const Timespan &span)
return parseCommentsFromQuery(selectDebugMessagesByTimespan);
}
vector<Comment> TraceDB::getDebugMessagesInTimespan(const Timespan &span,
vector<CommentModel::Comment> TraceDB::getDebugMessagesInTimespan(const Timespan &span,
unsigned int limit = 50)
{
selectDebugMessagesByTimespanWithLimit.bindValue(":begin", span.Begin());
@@ -432,12 +432,12 @@ vector<shared_ptr<Transaction>> TraceDB::parseTransactionsFromQuery(
return result;
}
vector<Comment> TraceDB::parseCommentsFromQuery(QSqlQuery &query)
vector<CommentModel::Comment> TraceDB::parseCommentsFromQuery(QSqlQuery &query)
{
vector<Comment> result;
vector<CommentModel::Comment> result;
while (query.next()) {
result.push_back(Comment(query.value(0).toLongLong(),
query.value(1).toString()));
result.push_back(CommentModel::Comment{query.value(0).toLongLong(),
query.value(1).toString()});
}
return result;
}

View File

@@ -50,7 +50,7 @@
#include "businessObjects/generalinfo.h"
#include "businessObjects/commandlengths.h"
#include "businessObjects/phases/phasefactory.h"
#include "businessObjects/comment.h"
#include "businessObjects/commentmodel.h"
#include "QueryTexts.h"
/* TraceDB handles the connection to a SQLLite database containing trace data.
@@ -68,7 +68,7 @@ public:
return pathToDB;
}
void updateComments(std::vector<Comment> comments);
void updateComments(const std::vector<CommentModel::Comment> &comments);
void updateFileDescription(const QString &description);
void refreshData();
@@ -98,9 +98,9 @@ public:
std::shared_ptr<Transaction> getTransactionByID(ID id);
ID getTransactionIDFromPhaseID(ID phaseID);
std::vector<Comment> getComments();
std::vector<Comment> getDebugMessagesInTimespan(const Timespan &span);
std::vector<Comment> getDebugMessagesInTimespan(const Timespan &span,
std::vector<CommentModel::Comment> getComments();
std::vector<CommentModel::Comment> getDebugMessagesInTimespan(const Timespan &span);
std::vector<CommentModel::Comment> getDebugMessagesInTimespan(const Timespan &span,
unsigned int limit);
QSqlDatabase getDatabase() const;
@@ -125,7 +125,7 @@ private:
std::shared_ptr<Transaction> parseTransactionFromQuery(QSqlQuery &query);
std::vector<std::shared_ptr<Transaction>> parseTransactionsFromQuery(
QSqlQuery &query);
std::vector<Comment> parseCommentsFromQuery(QSqlQuery &query);
std::vector<CommentModel::Comment> parseCommentsFromQuery(QSqlQuery &query);
void executeScriptFile(QString fileName);
void dropAndCreateTables();

View File

@@ -1,142 +0,0 @@
/*
* Copyright (c) 2015, Technische Universität Kaiserslautern
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* 3. Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER
* OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* Authors:
* Janik Schlemminger
* Robert Gernhardt
* Matthias Jung
* Derek Christ
*/
#include "commenttreewidget.h"
#include <QTreeWidgetItem>
#include <iostream>
#include <QMenu>
#include <QKeyEvent>
#include <QInputDialog>
#include <QLineEdit>
CommentTreeWidget::CommentTreeWidget(QWidget *parent) : QTreeWidget(parent),
deleteComment(new QAction("Delete comment", this)),
changeCommentText(new QAction("Change comment text", this))
{
connect(deleteComment, &QAction::triggered, this, [this](){
for (QTreeWidgetItem *item : selectedItems())
{
CommentTreeItem *commentItem = static_cast<CommentTreeItem *>(item);
navigator->removeCommentAtTime(commentItem->Time());
}
});
connect(changeCommentText, &QAction::triggered, this, [this](){
for (QTreeWidgetItem *item : selectedItems())
{
CommentTreeItem *commentItem = static_cast<CommentTreeItem *>(item);
bool ok;
QString newText = QInputDialog::getText(this, QString("Change comment text"),
QString("Change comment text"), QLineEdit::Normal,
QString(), &ok);
if (ok)
{
deleteComment->trigger();
navigator->insertComment({commentItem->Time(), newText});
}
commentsChanged();
}
});
}
void CommentTreeWidget::init(TraceNavigator *navigator)
{
Q_ASSERT(isInitialized == false);
isInitialized = true;
this->navigator = navigator;
setColumnCount(2);
setHeaderLabels(QStringList({"Time", "Comment"}));
QObject::connect(navigator, SIGNAL(commentsChanged()), this,
SLOT(commentsChanged()));
QObject::connect(this, SIGNAL(itemDoubleClicked(QTreeWidgetItem *, int)), this,
SLOT(itemDoubleClicked(QTreeWidgetItem *, int)));
QObject::connect(this, SIGNAL(customContextMenuRequested(QPoint)), this,
SLOT(ContextMenuRequested(QPoint)));
setContextMenuPolicy(Qt::CustomContextMenu);
printComments();
}
void CommentTreeWidget::commentsChanged()
{
clear();
printComments();
}
void CommentTreeWidget::printComments()
{
for (const auto &pair : navigator->getComments()) {
const Comment &comment = pair.second;
QTreeWidgetItem *item = new CommentTreeItem(this, comment);
addTopLevelItem(item);
}
resizeColumnToContents(0);
}
void CommentTreeWidget::itemDoubleClicked(QTreeWidgetItem *item, int /*column*/)
{
CommentTreeItem *commentItem = static_cast<CommentTreeItem *>(item);
navigator->navigateToTime(commentItem->Time());
}
void CommentTreeWidget::ContextMenuRequested(QPoint point)
{
QMenu contextMenu(this);
contextMenu.addActions({deleteComment, changeCommentText});
contextMenu.exec(mapToGlobal(point));
}
CommentTreeWidget::CommentTreeItem::CommentTreeItem(QTreeWidget *parent,
const Comment &comment): QTreeWidgetItem(parent), comment(comment)
{
this->setText(0, prettyFormatTime(comment.Time()));
this->setText(1, comment.Text());
}
void CommentTreeWidget::keyPressEvent(QKeyEvent *event)
{
if (event->key() == Qt::Key_Delete)
deleteComment->trigger();
else if (event->key() == Qt::Key_F2)
changeCommentText->trigger();
QTreeWidget::keyPressEvent(event);
}

View File

@@ -1,91 +0,0 @@
/*
* Copyright (c) 2015, Technische Universität Kaiserslautern
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* 3. Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER
* OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* Authors:
* Janik Schlemminger
* Robert Gernhardt
* Matthias Jung
* Derek Christ
*/
#ifndef COMMENTTREEWIDGET_H
#define COMMENTTREEWIDGET_H
#include <QTreeWidget>
#include <QTreeWidgetItem>
#include <QAction>
#include "businessObjects/comment.h"
#include "tracenavigator.h"
class CommentTreeWidget : public QTreeWidget
{
Q_OBJECT
public:
CommentTreeWidget(QWidget *parent = nullptr);
void init(TraceNavigator *navigator);
protected:
void keyPressEvent(QKeyEvent *event) override;
private Q_SLOTS:
void commentsChanged();
void ContextMenuRequested(QPoint point);
void itemDoubleClicked(QTreeWidgetItem *item, int column);
private:
bool isInitialized = false;
TraceNavigator *navigator;
void printComments();
QAction *deleteComment;
QAction *changeCommentText;
class CommentTreeItem : public QTreeWidgetItem
{
private:
Comment comment;
public:
CommentTreeItem(QTreeWidget *parent, const Comment &comment);
QString Text()
{
return comment.Text();
}
traceTime Time()
{
return comment.Time();
}
void changeText(QString newText)
{
comment.changeText(newText);
}
};
};
#endif // COMMENTTREEWIDGET_H

View File

@@ -84,19 +84,19 @@ void DebugMessageTreeWidget::currentTraceTimeChanged()
traceplot->GetCurrentTimespan()));
}
void DebugMessageTreeWidget::showDebugMessages(const vector<Comment> &comments)
void DebugMessageTreeWidget::showDebugMessages(const vector<CommentModel::Comment> &comments)
{
clear();
if (comments.empty())
return;
traceTime currentTime = -1;
for (Comment comment : comments) {
if (currentTime != comment.Time()) {
addTopLevelItem(new QTreeWidgetItem({prettyFormatTime(comment.Time()), formatDebugMessage(comment.Text())}));
currentTime = comment.Time();
for (const auto &comment : comments) {
if (currentTime != comment.time) {
addTopLevelItem(new QTreeWidgetItem({prettyFormatTime(comment.time), formatDebugMessage(comment.text)}));
currentTime = comment.time;
} else {
addTopLevelItem(new QTreeWidgetItem({"", formatDebugMessage(comment.Text())}));
addTopLevelItem(new QTreeWidgetItem({"", formatDebugMessage(comment.text)}));
}
}

View File

@@ -42,7 +42,7 @@
#include <QString>
#include <QRegularExpression>
#include <QMouseEvent>
#include "businessObjects/comment.h"
#include "businessObjects/commentmodel.h"
#include "tracenavigator.h"
#include "traceplot.h"
@@ -56,7 +56,7 @@ public:
hexAdressMatcher(QString("0x[0-9,a-f]+")) {}
void init(TraceNavigator *navigator, TracePlot *traceplot);
void showDebugMessages(const std::vector<Comment> &comments);
void showDebugMessages(const std::vector<CommentModel::Comment> &comments);
void arrangeUiSettings();
public Q_SLOTS:

View File

@@ -39,14 +39,24 @@
#include "tracenavigator.h"
#include "util/traceplotlinecache.h"
#include "vector"
#include "businessObjects/commentmodel.h"
#include <QInputDialog>
#include <QLineEdit>
using namespace std;
TraceNavigator::TraceNavigator(QString path, QObject *parent)
: QObject(parent), traceFile(path, true), changesToCommitExist(false)
TraceNavigator::TraceNavigator(QString path, CommentModel *commentModel, QObject *parent)
: QObject(parent), traceFile(path, true), commentModel(commentModel), changesToCommitExist(false)
{
getCommentsFromDB();
QObject::connect(commentModel, &CommentModel::gotoCommentTriggered, this, [=](const QModelIndex &index){
navigateToTime(commentModel->getTimeFromIndex(index));
});
QObject::connect(commentModel, &CommentModel::dataChanged, this, &TraceNavigator::traceFileModified);
QObject::connect(commentModel, &CommentModel::rowsRemoved, this, &TraceNavigator::traceFileModified);
tracePlotLineCache =
std::make_shared<TracePlotLineCache>(getTracePlotLines(), GeneralTraceInfo().numberOfRanks,
GeneralTraceInfo().groupsPerRank, GeneralTraceInfo().banksPerGroup);
@@ -79,49 +89,20 @@ void TraceNavigator::navigateToTransaction(ID id)
navigateToTime(traceFile.getTransactionByID(id)->span.Begin());
}
/* Comment and debug messages
*
*/
void TraceNavigator::insertComment(const Comment &comment)
{
comments.emplace(comment.Time(), comment);
changesToCommitExist = true;
Q_EMIT commentsChanged();
}
void TraceNavigator::removeCommentAtTime(traceTime time)
{
auto found = comments.find(time);
if (found != comments.end()) {
comments.erase(found);
changesToCommitExist = true;
Q_EMIT commentsChanged();
}
}
/* DB
*
*/
void TraceNavigator::commitChangesToDB()
{
vector<Comment> commentsToInsert;
for (const auto &pair : comments) {
commentsToInsert.push_back(pair.second);
}
traceFile.updateComments(commentsToInsert);
traceFile.updateComments(commentModel->getComments());
changesToCommitExist = false;
}
void TraceNavigator::getCommentsFromDB()
{
for (const Comment &comment : traceFile.getComments()) {
comments.emplace(comment.Time(), comment);
}
for (const auto &comment : traceFile.getComments())
commentModel->addComment(comment.time, comment.text);
}
void TraceNavigator::refreshData()
@@ -334,3 +315,18 @@ std::shared_ptr<TracePlotLineCache> TraceNavigator::getTracePlotLineCache()
{
return tracePlotLineCache;
}
const CommentModel *TraceNavigator::getCommentModel() const
{
return commentModel;
}
bool TraceNavigator::existChangesToCommit() const
{
return changesToCommitExist;
}
void TraceNavigator::traceFileModified()
{
changesToCommitExist = true;
}

View File

@@ -43,10 +43,10 @@
#include "data/tracedb.h"
#include "businessObjects/generalinfo.h"
#include "businessObjects/transaction.h"
#include "businessObjects/comment.h"
#include "memory"
class TracePlotLineCache;
class CommentModel;
/* Class to navigate through a tracefile
*
@@ -56,10 +56,9 @@ class TracePlotLineCache;
class TraceNavigator : public QObject
{
Q_OBJECT
using CommentMap = std::map<traceTime, Comment>;
public:
TraceNavigator(QString path, QObject *parent = 0);
TraceNavigator(QString path, CommentModel *commentModel, QObject *parent = 0);
~TraceNavigator();
traceTime CurrentTraceTime() const
@@ -111,23 +110,22 @@ public:
bool transactionIsSelected(const std::shared_ptr<Transaction> &Transaction)
const;
void insertComment(const Comment &comment);
const CommentMap &getComments()
{
return comments;
}
void removeCommentAtTime(traceTime time);
void commitChangesToDB();
void refreshData();
std::shared_ptr<TracePlotLineVector> getTracePlotLines();
std::shared_ptr<TracePlotLineCache> getTracePlotLineCache();
const CommentModel *getCommentModel() const;
bool existChangesToCommit() const;
Q_SIGNALS:
void currentTraceTimeChanged();
void selectedTransactionsChanged();
void commentsChanged();
public Q_SLOTS:
void traceFileModified();
private:
TraceDB traceFile;
@@ -136,7 +134,9 @@ private:
//components drawing the tracefile center around that time
traceTime currentTraceTime = 0;
std::vector<std::shared_ptr<Transaction>> selectedTransactions;
CommentMap comments;
CommentModel *commentModel;
void getCommentsFromDB();
bool changesToCommitExist;

View File

@@ -55,6 +55,7 @@
#include "traceplot.h"
#include "gototimedialog.h"
#include "tracedrawing.h"
#include "businessObjects/commentmodel.h"
#include "util/engineeringScaleDraw.h"
#include "util/clkgrid.h"
#include "util/customlabelscaledraw.h"
@@ -184,12 +185,24 @@ void TracePlot::setUpContextMenu()
contextMenu->addActions({showQueryEditor, insertComment, exportToPdf, toggleCollapsedState});
}
void TracePlot::init(TraceNavigator *navigator, QScrollBar *scrollBar)
void TracePlot::init(TraceNavigator *navigator, QScrollBar *scrollBar, CommentModel *commentModel)
{
Q_ASSERT(isInitialized == false);
isInitialized = true;
this->scrollBar = scrollBar;
this->commentModel = commentModel;
installEventFilter(commentModel);
QObject::connect(commentModel, &CommentModel::dataChanged,
this, &TracePlot::commentsChanged);
QObject::connect(commentModel, &CommentModel::rowsRemoved,
this, &TracePlot::commentsChanged);
QObject::connect(commentModel->selectionModel(), &QItemSelectionModel::selectionChanged,
this, &TracePlot::commentsChanged);
this->navigator = navigator;
connectNavigatorQ_SIGNALS();
@@ -216,8 +229,6 @@ void TracePlot::connectNavigatorQ_SIGNALS()
SLOT(currentTraceTimeChanged()));
QObject::connect(navigator, SIGNAL(selectedTransactionsChanged()), this,
SLOT(selectedTransactionsChanged()));
QObject::connect(navigator, SIGNAL(commentsChanged()), this,
SLOT(commentsChanged()));
}
void TracePlot::setUpDrawingProperties()
@@ -343,16 +354,23 @@ Timespan TracePlot::GetCurrentTimespan()
void TracePlot::getAndDrawComments()
{
for (const auto &pair : navigator->getComments()) {
const Comment &comment = pair.second;
QwtPlotMarker *maker = new QwtPlotMarker();
maker->setLabel(comment.Text());
maker->setLabelOrientation(Qt::Vertical);
maker->setLabelAlignment(Qt::AlignLeft | Qt::AlignBottom);
maker->setXValue(static_cast<double>(comment.Time()));
maker->setLineStyle(QwtPlotMarker::LineStyle::VLine);
maker->setLinePen(QColor(Qt::blue), 2);
maker->attach(this);
QList<QModelIndex> selectedRows = commentModel->selectionModel()->selectedRows();
for (int row = 0; row < commentModel->rowCount(); row++)
{
QModelIndex timeIndex = commentModel->index(row, static_cast<int>(CommentModel::Column::Time));
QModelIndex textIndex = commentModel->index(row, static_cast<int>(CommentModel::Column::Comment));
bool selected = std::find(selectedRows.begin(), selectedRows.end(), commentModel->index(row, 0)) != selectedRows.end();
QwtPlotMarker *marker = new QwtPlotMarker();
marker->setLabel(textIndex.data().toString());
marker->setLabelOrientation(Qt::Vertical);
marker->setLabelAlignment(Qt::AlignLeft | Qt::AlignBottom);
marker->setXValue(static_cast<double>(timeIndex.data(Qt::UserRole).toLongLong()));
marker->setLineStyle(QwtPlotMarker::LineStyle::VLine);
marker->setLinePen(QColor(selected ? Qt::red : Qt::blue), 2);
marker->attach(this);
}
}
@@ -541,13 +559,7 @@ void TracePlot::on_deselectAll()
void TracePlot::on_insertComment()
{
traceTime time = invTransform(xBottom, contextMenuMouseDown.x());
bool ok;
QString text = QInputDialog::getText(this, QString("Insert comment"),
QString("Insert comment at ") + prettyFormatTime(time), QLineEdit::Normal,
QString(), &ok);
if (ok) {
navigator->insertComment(Comment(time, text));
}
commentModel->addComment(time);
}
void TracePlot::on_goToTime()
@@ -646,9 +658,13 @@ bool TracePlot::eventFilter(QObject *object, QEvent *event)
mouseDownData.mouseIsDownForDragging = true;
canvas()->setCursor(Qt::ClosedHandCursor);
SelectTransaction(mouseEvent->x(), mouseEvent->y());
SelectComment(mouseEvent->x());
}
return true;
} else if (mouseEvent->button() == Qt::RightButton) {
// Also select comments to make it more obvious.
SelectComment(mouseEvent->x());
openContextMenu(this->canvas()->mapToGlobal(mouseEvent->pos()),
mouseEvent->pos());
return true;
@@ -704,7 +720,7 @@ bool TracePlot::eventFilter(QObject *object, QEvent *event)
return false;
}
void TracePlot::SelectTransaction(int x, int y)
void TracePlot::SelectTransaction(int x, int y) const
{
double yVal = invTransform(yLeft, y);
traceTime time = invTransform(xBottom, x);
@@ -717,10 +733,35 @@ void TracePlot::SelectTransaction(int x, int y)
}
}
void TracePlot::SelectComment(int x) const
{
Timespan timespan = hoveredTimespan(x);
QModelIndex index = commentModel->hoveredComment(timespan);
if (!index.isValid())
return;
if (keyPressData.ctrlPressed)
commentModel->selectionModel()->setCurrentIndex(index, QItemSelectionModel::Toggle | QItemSelectionModel::Rows);
else
commentModel->selectionModel()->setCurrentIndex(index, QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows);
}
Timespan TracePlot::hoveredTimespan(int x) const
{
traceTime time = invTransform(xBottom, x);
traceTime offset = 0.01 * zoomLevel;
return Timespan(time - offset, time + offset);
}
void TracePlot::openContextMenu(const QPoint &pos, const QPoint &mouseDown)
{
contextMenuMouseDown = mouseDown;
contextMenu->exec(pos);
Timespan timespan = hoveredTimespan(mouseDown.x());
if (commentModel->hoveredComment(timespan).isValid())
commentModel->openContextMenu();
else
contextMenu->exec(pos);
}

View File

@@ -63,6 +63,7 @@
class TracePlotMouseLabel;
class CustomLabelScaleDraw;
class ToggleCollapsedAction;
class CommentModel;
class TracePlot : public QwtPlot
{
@@ -71,7 +72,7 @@ class TracePlot : public QwtPlot
public:
TracePlot(QWidget *parent = NULL);
void init(TraceNavigator *navigator, QScrollBar *scrollBar);
void init(TraceNavigator *navigator, QScrollBar *scrollBar, CommentModel *commentModel);
Timespan GetCurrentTimespan();
traceTime ZoomLevel() const
{
@@ -97,6 +98,7 @@ Q_SIGNALS:
void tracePlotZoomChanged();
void tracePlotLinesChanged();
void colorGroupingChanged(ColorGrouping colorgrouping);
// void editComment
private Q_SLOTS:
void on_executeQuery();
@@ -129,6 +131,7 @@ private:
QMenu *contextMenu;
QScrollBar *scrollBar;
CustomLabelScaleDraw *customLabelScaleDraw;
CommentModel *commentModel;
void setUpTracePlotItem();
void setUpDrawingProperties();
@@ -186,7 +189,10 @@ private:
void openContextMenu(const QPoint &pos, const QPoint &mouseDown);
QPoint contextMenuMouseDown;
void SelectTransaction(int x, int y);
void SelectTransaction(int x, int y) const;
void SelectComment(int x) const;
//const std::vector<std::shared_ptr<Comment>> hoveredComments(traceTime time) const;
Timespan hoveredTimespan(int x) const;
void keyPressEvent(QKeyEvent *keyPressedEvent);
void keyReleaseEvent(QKeyEvent *keyReleasedEvent);

View File

@@ -62,6 +62,17 @@ void TraceScroller::init(TraceNavigator *navigator, TracePlot *tracePlot)
this -> navigator = navigator;
connectNavigatorQ_SIGNALS();
const CommentModel *commentModel = navigator->getCommentModel();
QObject::connect(commentModel, &CommentModel::dataChanged,
this, &TraceScroller::commentsChanged);
QObject::connect(commentModel, &CommentModel::rowsRemoved,
this, &TraceScroller::commentsChanged);
QObject::connect(commentModel->selectionModel(), &QItemSelectionModel::selectionChanged,
this, &TraceScroller::commentsChanged);
setUpDrawingProperties();
setUpAxis();
setUpTracePlotItem();
@@ -119,8 +130,7 @@ void TraceScroller::connectNavigatorQ_SIGNALS()
{
QObject::connect(navigator, SIGNAL(currentTraceTimeChanged()), this,
SLOT(currentTraceTimeChanged()));
QObject::connect(navigator, SIGNAL(commentsChanged()), this,
SLOT(commentsChanged()));
QObject::connect(navigator, SIGNAL(selectedTransactionsChanged()), this,
SLOT(selectedTransactionsChanged()));
}
@@ -147,12 +157,19 @@ Timespan TraceScroller::GetCurrentTimespan()
void TraceScroller::getAndDrawComments()
{
for (const auto &pair : navigator->getComments()) {
const Comment &comment = pair.second;
const CommentModel *commentModel = navigator->getCommentModel();
QList<QModelIndex> selectedRows = commentModel->selectionModel()->selectedRows();
for (int row = 0; row < commentModel->rowCount(); row++)
{
QModelIndex timeIndex = commentModel->index(row, static_cast<int>(CommentModel::Column::Time));
bool selected = std::find(selectedRows.begin(), selectedRows.end(), commentModel->index(row, 0)) != selectedRows.end();
QwtPlotMarker *maker = new QwtPlotMarker();
maker->setXValue(static_cast<double>(comment.Time()));
maker->setXValue(static_cast<double>(timeIndex.data(Qt::UserRole).toLongLong()));
maker->setLineStyle(QwtPlotMarker::LineStyle::VLine);
maker->setLinePen(QColor(Qt::blue), 2);
maker->setLinePen(QColor(selected ? Qt::red : Qt::blue), 2);
maker->attach(this);
}
}

View File

@@ -60,7 +60,6 @@ private:
void setUpAxis();
void connectNavigatorQ_SIGNALS();
void getAndDrawComments();
QwtPlotZoneItem *canvasClip;
traceTime zoomLevel;

View File

@@ -37,12 +37,13 @@
*/
#include "traceanalyzer.h"
#include "ui_traceanalyzer.h"
#include "QMessageBox"
#include "tracefiletab.h"
#include "ui_traceanalyzer.h"
#include <QCloseEvent>
#include <QDateTime>
#include <QFileDialog>
#include <QFileInfo>
#include <QDateTime>
#include "QMessageBox"
#include <iostream>
void TraceAnalyzer::setUpStatusBar()
@@ -64,13 +65,6 @@ TraceAnalyzer::TraceAnalyzer(QWidget *parent) :
ui(new Ui::TraceAnalyzer)
{
setUpGui();
// Disable actions except for "Open" until some file is open.
ui->actionReload_all->setEnabled(false);
ui->actionSaveChangesToDB->setEnabled(false);
ui->actionExportAsVCD->setEnabled(false);
ui->actionClose_all->setEnabled(false);
ui->actionTest->setEnabled(false);
ui->actionMetrics->setEnabled(false);
}
TraceAnalyzer::TraceAnalyzer(QSet<QString> paths, StartupOption option,
@@ -119,41 +113,43 @@ void TraceAnalyzer::openTracefile(const QString &path)
ui->traceFileTabs->addTab(tab, QFileInfo(path).baseName());
openedTraceFiles.insert(path);
// Enable actions
ui->actionReload_all->setEnabled(true);
ui->actionSaveChangesToDB->setEnabled(true);
if (PythonCaller::instance().vcdExportDependenciesAvailable())
ui->actionExportAsVCD->setEnabled(true);
ui->actionClose_all->setEnabled(true);
ui->actionTest->setEnabled(true);
ui->actionMetrics->setEnabled(true);
statusLabel->clear();
}
void TraceAnalyzer::on_menuFile_aboutToShow()
{
ui->actionOpen->setEnabled(true);
bool tabsOpen = ui->traceFileTabs->count() > 0;
bool exportAsVcdAvailable = PythonCaller::instance().vcdExportDependenciesAvailable();
ui->actionReload_all->setEnabled(tabsOpen);
ui->actionSaveChangesToDB->setEnabled(tabsOpen);
ui->actionExportAsVCD->setEnabled(tabsOpen && exportAsVcdAvailable);
ui->actionClose_all->setEnabled(tabsOpen);
ui->actionTest->setEnabled(tabsOpen);
ui->actionMetrics->setEnabled(tabsOpen);
}
void TraceAnalyzer::on_traceFileTabs_tabCloseRequested(int index)
{
TraceFileTab *traceFileTab = static_cast<TraceFileTab *>
(ui->traceFileTabs->widget(index));
openedTraceFiles.remove(traceFileTab->getPathToTraceFile());
ui->traceFileTabs->removeTab(index);
delete traceFileTab;
if (traceFileTab->close())
{
openedTraceFiles.remove(traceFileTab->getPathToTraceFile());
ui->traceFileTabs->removeTab(index);
delete traceFileTab;
}
}
void TraceAnalyzer::on_actionClose_all_triggered()
{
while (ui->traceFileTabs->count())
on_traceFileTabs_tabCloseRequested(0);
// All files closed. Disable actions except for "Open".
ui->actionReload_all->setEnabled(false);
ui->actionSaveChangesToDB->setEnabled(false);
ui->actionExportAsVCD->setEnabled(false);
ui->actionClose_all->setEnabled(false);
ui->actionTest->setEnabled(false);
ui->actionMetrics->setEnabled(false);
for (unsigned int i = ui->traceFileTabs->count(); i--;)
{
on_traceFileTabs_tabCloseRequested(i);
}
statusLabel->clear();
}
@@ -163,23 +159,24 @@ void TraceAnalyzer::on_actionReload_all_triggered()
TraceFileTab *traceFileTab;
// Remove all tabs
int tabIndex = 0;
while (ui->traceFileTabs->count() != 0) {
for (unsigned int i = ui->traceFileTabs->count(); i--;)
{
traceFileTab = static_cast<TraceFileTab *>(ui->traceFileTabs->widget(0));
std::cout << "Closing tab #" << tabIndex << " \"" <<
traceFileTab->getPathToTraceFile().toStdString() << "\"" << std::endl;
std::cout << "Closing tab #" << i << " \"" << traceFileTab->getPathToTraceFile().toStdString() << "\""
<< std::endl;
if (!traceFileTab->close())
return;
ui->traceFileTabs->removeTab(0);
delete traceFileTab;
tabIndex++;
}
QList<QString> list = openedTraceFiles.toList();
qSort(list);
// Recreate all tabs
tabIndex = 0;
int tabIndex = 0;
for (auto path : list) {
std::cout << "Reopening tab# " << tabIndex << " \"" << path.toStdString() <<
"\"" << std::endl;
@@ -231,3 +228,18 @@ void TraceAnalyzer::on_actionMetrics_triggered()
evaluationTool.activateWindow();
evaluationTool.showAndEvaluateMetrics(openedTraceFiles.toList());
}
void TraceAnalyzer::closeEvent(QCloseEvent *event)
{
for (unsigned int i = 0; i < ui->traceFileTabs->count(); i++)
{
QWidget *tab = ui->traceFileTabs->widget(i);
if (!tab->close())
{
event->ignore();
return;
}
}
event->accept();
}

View File

@@ -67,20 +67,24 @@ public:
void setUpStatusBar();
void setUpGui();
protected:
void closeEvent(QCloseEvent *event) override;
private:
void openTracefile(const QString &path);
QLabel *statusLabel;
QSet<QString> openedTraceFiles;
private Q_SLOTS:
void on_menuFile_aboutToShow();
void on_traceFileTabs_tabCloseRequested(int index);
void on_actionOpen_triggered();
void on_actionReload_all_triggered();
void on_actionSaveChangesToDB_triggered();
void on_actionExportAsVCD_triggered();
void on_traceFileTabs_tabCloseRequested(int index);
void on_actionClose_all_triggered();
void on_actionTest_triggered();
void on_actionMetrics_triggered();
public Q_SLOTS:

View File

@@ -37,37 +37,37 @@
*/
#include "tracefiletab.h"
#include "ui_tracefiletab.h"
#include "queryeditor.h"
#include "QFileInfo"
#include "qmessagebox.h"
#include "businessObjects/commentmodel.h"
#include "businessObjects/configmodels.h"
#include "businessObjects/pythoncaller.h"
#include <iostream>
#include <QStandardItemModel>
#include <QString>
#include <QItemDelegate>
#include <QFileDialog>
#include <QtConcurrent/QtConcurrent>
#include <QTextStream>
#include <QDebug>
#include "qwt_plot_histogram.h"
#include "qwt_plot_curve.h"
#include "qwt_plot_layout.h"
#include "qwt_scale_draw.h"
#include "qwt_scale_widget.h"
#include "presentation/traceselector.h"
#include "qmessagebox.h"
#include "queryeditor.h"
#include "qwt_legend.h"
#include "qwt_plot_curve.h"
#include "qwt_plot_histogram.h"
#include "qwt_plot_layout.h"
#include "qwt_plot_magnifier.h"
#include "qwt_plot_panner.h"
#include "presentation/traceselector.h"
#include "businessObjects/configmodels.h"
#include "qwt_scale_draw.h"
#include "qwt_scale_widget.h"
#include "ui_tracefiletab.h"
#include <QDebug>
#include <QFileDialog>
#include <QItemDelegate>
#include <QMessageBox>
#include <QStandardItemModel>
#include <QString>
#include <QTextStream>
#include <QtConcurrent/QtConcurrent>
#include <iostream>
#include <math.h>
TraceFileTab::TraceFileTab(QWidget *parent, const QString &path) :
QWidget(parent), ui(new Ui::TraceFileTab), navigator(new TraceNavigator(path, this)),
mcConfigModel(new McConfigModel(navigator->TraceFile(), this)),
memSpecModel(new MemSpecModel(navigator->TraceFile(), this)),
savingChangesToDB(false)
TraceFileTab::TraceFileTab(QWidget *parent, const QString &path)
: QWidget(parent), ui(new Ui::TraceFileTab), commentModel(new CommentModel(this)),
navigator(new TraceNavigator(path, commentModel, this)),
mcConfigModel(new McConfigModel(navigator->TraceFile(), this)),
memSpecModel(new MemSpecModel(navigator->TraceFile(), this)), savingChangesToDB(false)
{
ui->setupUi(this);
this->path = path;
@@ -79,6 +79,7 @@ TraceFileTab::TraceFileTab(QWidget *parent, const QString &path) :
setUpFileWatcher(path);
setUpTraceplotScrollbar();
setUpTraceSelector();
setUpCommentView();
ui->fileDescriptionEdit->setPlainText(
navigator->GeneralTraceInfo().description);
@@ -132,7 +133,7 @@ void TraceFileTab::setUpTraceplotScrollbar()
void TraceFileTab::initNavigatorAndItsDependentWidgets(QString path)
{
ui->traceplot->init(navigator, ui->traceplotScrollbar);
ui->traceplot->init(navigator, ui->traceplotScrollbar, commentModel);
ui->traceScroller->init(navigator, ui->traceplot);
connect(this, SIGNAL(colorGroupingChanged(ColorGrouping)),
@@ -140,7 +141,6 @@ void TraceFileTab::initNavigatorAndItsDependentWidgets(QString path)
ui->selectedTransactionTree->init(navigator);
//ui->debugMessages->init(navigator,ui->traceplot);
ui->commentTree->init(navigator);
}
void TraceFileTab::setUpFileWatcher(QString path)
@@ -158,6 +158,26 @@ void TraceFileTab::setUpTraceSelector()
selector->init(ui->traceplot);
}
void TraceFileTab::setUpCommentView()
{
ui->commentView->setModel(commentModel);
ui->commentView->setSelectionModel(commentModel->selectionModel());
ui->commentView->installEventFilter(commentModel);
ui->commentView->setContextMenuPolicy(Qt::CustomContextMenu);
QObject::connect(ui->commentView, &QTableView::customContextMenuRequested,
commentModel, &CommentModel::openContextMenu);
QObject::connect(commentModel, &CommentModel::editTriggered, ui->commentView, [=](const QModelIndex &index){
ui->tabWidget->setCurrentWidget(ui->tabSelectedTrans);
ui->commentView->edit(index);
ui->commentView->scrollTo(index);
});
QObject::connect(ui->commentView, &QTableView::doubleClicked,
commentModel, &CommentModel::rowDoubleClicked);
}
void TraceFileTab::tracefileChanged()
{
if (savingChangesToDB == true) {
@@ -176,6 +196,37 @@ void TraceFileTab::tracefileChanged()
navigator->refreshData();
}
void TraceFileTab::closeEvent(QCloseEvent *event)
{
if (navigator->existChangesToCommit())
{
QMessageBox saveDialog;
saveDialog.setWindowTitle(QFileInfo(path).baseName());
saveDialog.setText("The trace file has been modified.");
saveDialog.setInformativeText("Do you want to save your changes?<br><b>Unsaved changes will be lost.</b>");
saveDialog.setStandardButtons(QMessageBox::Save | QMessageBox::Discard | QMessageBox::Cancel);
saveDialog.setDefaultButton(QMessageBox::Save);
saveDialog.setIcon(QMessageBox::Warning);
int returnCode = saveDialog.exec();
switch (returnCode)
{
case QMessageBox::Cancel:
event->ignore();
break;
case QMessageBox::Discard:
event->accept();
break;
case QMessageBox::Save:
commitChangesToDB();
event->accept();
break;
};
}
else
event->accept();
}
class ItemDelegate: public QItemDelegate
{
public:

View File

@@ -47,6 +47,8 @@
#include "presentation/tracescroller.h"
#include "businessObjects/configmodels.h"
class CommentModel;
namespace Ui {
class TraceFileTab;
}
@@ -62,6 +64,7 @@ public:
void setUpFileWatcher(QString filename);
void setUpTraceplotScrollbar();
void setUpTraceSelector();
void setUpCommentView();
void initNavigatorAndItsDependentWidgets(QString path);
QString getPathToTraceFile()
{
@@ -70,12 +73,17 @@ public:
void commitChangesToDB(void);
void exportAsVCD();
protected:
void closeEvent(QCloseEvent *event) override;
private:
QString path;
Ui::TraceFileTab *ui;
TraceNavigator *navigator;
QFileSystemWatcher *fileWatcher;
CommentModel *commentModel;
TraceNavigator *navigator;
QAbstractItemModel *mcConfigModel;
QAbstractItemModel *memSpecModel;

View File

@@ -170,7 +170,7 @@
</widget>
</item>
<item>
<widget class="CommentTreeWidget" name="commentTree">
<widget class="QTableView" name="commentView">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Minimum">
<horstretch>2</horstretch>
@@ -189,11 +189,27 @@
<height>16777215</height>
</size>
</property>
<column>
<property name="text">
<string notr="true">1</string>
</property>
</column>
<property name="editTriggers">
<set>QAbstractItemView::DoubleClicked|QAbstractItemView::EditKeyPressed</set>
</property>
<property name="alternatingRowColors">
<bool>true</bool>
</property>
<property name="selectionBehavior">
<enum>QAbstractItemView::SelectRows</enum>
</property>
<property name="gridStyle">
<enum>Qt::NoPen</enum>
</property>
<property name="sortingEnabled">
<bool>false</bool>
</property>
<attribute name="horizontalHeaderStretchLastSection">
<bool>true</bool>
</attribute>
<attribute name="verticalHeaderVisible">
<bool>false</bool>
</attribute>
</widget>
</item>
<item>
@@ -402,11 +418,6 @@
<extends>QListView</extends>
<header>presentation/tracescroller.h</header>
</customwidget>
<customwidget>
<class>CommentTreeWidget</class>
<extends>QTreeWidget</extends>
<header>presentation/commenttreewidget.h</header>
</customwidget>
<customwidget>
<class>SelectedTransactionTreeWidget</class>
<extends>QTreeWidget</extends>