LogsWindow: Improve UX
* Timestamp at millisec resolution * Log level selection - Info by default * Auto scroll control - enable(default)/disable * Support copy (selected) logs to clipboard * Support clear logs * Annotate dock window title, if not on top (aka visible)
This commit is contained in:
parent
489099ca83
commit
509d777500
@ -36,17 +36,13 @@ static QStringList columnTitles = QStringList()
|
||||
<< "Message";
|
||||
|
||||
static QStringList levelTitles = QStringList()
|
||||
<< "Error"
|
||||
<< "Info"
|
||||
<< "Warning"
|
||||
<< "Info";
|
||||
<< "Error";
|
||||
|
||||
LogsModel::LogsModel(QObject *parent)
|
||||
: QAbstractTableModel(parent)
|
||||
{
|
||||
// FIXME: test only
|
||||
log(kInfo, QString("--"), QString("Welcome to Ostinato!"));
|
||||
log(kWarning, QString("--"), QString("This is a sample warning!"));
|
||||
log(kError, QString("--"), QString("This is a sample error!"));
|
||||
}
|
||||
|
||||
int LogsModel::rowCount(const QModelIndex &/*parent*/) const
|
||||
@ -56,9 +52,6 @@ int LogsModel::rowCount(const QModelIndex &/*parent*/) const
|
||||
|
||||
int LogsModel::columnCount(const QModelIndex &/*parent*/) const
|
||||
{
|
||||
if (!logs_.size())
|
||||
return 0;
|
||||
|
||||
return kFieldCount;
|
||||
}
|
||||
|
||||
@ -81,6 +74,9 @@ QVariant LogsModel::headerData(
|
||||
|
||||
QVariant LogsModel::data(const QModelIndex &index, int role) const
|
||||
{
|
||||
if (!index.isValid() || (index.row() >= logs_.size()))
|
||||
return QVariant();
|
||||
|
||||
if (role == Qt::ForegroundRole) {
|
||||
switch(logs_.at(index.row()).logLevel) {
|
||||
case kError:
|
||||
@ -98,7 +94,7 @@ QVariant LogsModel::data(const QModelIndex &index, int role) const
|
||||
|
||||
switch (index.column()) {
|
||||
case kTimeStamp:
|
||||
return logs_.at(index.row()).timeStamp.toString();
|
||||
return logs_.at(index.row()).timeStamp.toString("hh:mm:ss.zzz");
|
||||
case kLogLevel:
|
||||
return levelTitles.at(logs_.at(index.row()).logLevel);
|
||||
case kPort:
|
||||
@ -122,8 +118,19 @@ void LogsModel::clear()
|
||||
endResetModel();
|
||||
}
|
||||
|
||||
void LogsModel::setLogLevel(int level)
|
||||
{
|
||||
currentLevel_ = static_cast<LogLevel>(level % kLevelCount);
|
||||
log(currentLevel_, QString("--"),
|
||||
QString("Log level changed to %1 or higher")
|
||||
.arg(levelTitles.at(currentLevel_)));
|
||||
}
|
||||
|
||||
void LogsModel::log(int logLevel,QString port, QString message)
|
||||
{
|
||||
if (logLevel < currentLevel_)
|
||||
return;
|
||||
|
||||
// TODO: discard logs older than some threshold
|
||||
|
||||
//qDebug("adding log %u %s", logs_.size(), qPrintable(message));
|
||||
|
@ -28,9 +28,10 @@ class LogsModel: public QAbstractTableModel
|
||||
Q_OBJECT
|
||||
public:
|
||||
enum LogLevel { // FIXME: use enum class?
|
||||
kError,
|
||||
kInfo,
|
||||
kWarning,
|
||||
kInfo
|
||||
kError,
|
||||
kLevelCount
|
||||
};
|
||||
|
||||
public:
|
||||
@ -45,6 +46,7 @@ public:
|
||||
|
||||
public slots:
|
||||
void clear();
|
||||
void setLogLevel(int level);
|
||||
void log(int logLevel,QString port, QString message);
|
||||
|
||||
private:
|
||||
@ -55,6 +57,7 @@ private:
|
||||
QString message;
|
||||
};
|
||||
QVector<Log> logs_;
|
||||
LogLevel currentLevel_{kInfo};
|
||||
};
|
||||
#endif
|
||||
|
||||
|
@ -20,26 +20,95 @@ along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
#include "logswindow.h"
|
||||
|
||||
#include "logsmodel.h"
|
||||
#include "modeltest.h"
|
||||
|
||||
#include <QDockWidget>
|
||||
#include <QHeaderView>
|
||||
|
||||
LogsWindow::LogsWindow(LogsModel *model, QWidget *parent)
|
||||
: QWidget(parent)
|
||||
{
|
||||
setupUi(this);
|
||||
logs->setModel(model);
|
||||
autoScroll->setChecked(true);
|
||||
|
||||
logs->verticalHeader()->setHighlightSections(false);
|
||||
logs->verticalHeader()->setDefaultSectionSize(
|
||||
logs->verticalHeader()->minimumSectionSize());
|
||||
logs->setShowGrid(false);
|
||||
logs->setAlternatingRowColors(true);
|
||||
logs->setModel(model);
|
||||
logs->horizontalHeader()->setSectionResizeMode(3, QHeaderView::Stretch);
|
||||
|
||||
//FIXME: connect(clear, SIGNAL(clicked()), model, SLOT(clear()));
|
||||
parentDock_ = qobject_cast<QDockWidget*>(parent);
|
||||
windowTitle_ = parentDock_->windowTitle();
|
||||
|
||||
connect(level, SIGNAL(currentIndexChanged(int)),
|
||||
model, SLOT(setLogLevel(int)));
|
||||
connect(clear, SIGNAL(clicked()), model, SLOT(clear()));
|
||||
|
||||
connect(parentDock_, SIGNAL(visibilityChanged(bool)),
|
||||
SLOT(when_visibilityChanged(bool)));
|
||||
connect(model, SIGNAL(rowsInserted(const QModelIndex&, int, int)),
|
||||
SLOT(when_rowsInserted(const QModelIndex&, int, int)));
|
||||
|
||||
#if defined(QT_NO_DEBUG) || QT_VERSION < 0x050700
|
||||
logsModelTest_ = nullptr;
|
||||
#else
|
||||
logsModelTest_ = new ModelTest(model);
|
||||
#endif
|
||||
}
|
||||
|
||||
LogsWindow::~LogsWindow()
|
||||
{
|
||||
delete logsModelTest_;
|
||||
}
|
||||
|
||||
void LogsWindow::when_visibilityChanged(bool visible)
|
||||
{
|
||||
if (visible) {
|
||||
parentDock_->setWindowTitle(windowTitle_);
|
||||
annotation_.clear();
|
||||
}
|
||||
|
||||
isVisible_ = visible;
|
||||
qDebug("isVisible = %u", isVisible_);
|
||||
}
|
||||
|
||||
void LogsWindow::when_rowsInserted(const QModelIndex &parent,
|
||||
int first, int last)
|
||||
{
|
||||
if (isVisible_)
|
||||
return;
|
||||
|
||||
if (annotation_.contains("Error"))
|
||||
return;
|
||||
|
||||
for (int i = first; i <= last; i++) {
|
||||
// FIXME: use a user-role instead, so we don't need to know column and
|
||||
// have to compare strings?
|
||||
QString level = logs->model()->data(logs->model()->index(i, 1, parent))
|
||||
.toString();
|
||||
if (level == "Error") {
|
||||
annotation_ = QString(" - Error(s)");
|
||||
break; // Highest level - no need to look further
|
||||
}
|
||||
else if (level == "Warning") {
|
||||
annotation_ = QString(" - Warning(s)");
|
||||
}
|
||||
}
|
||||
parentDock_>setWindowTitle(windowTitle_+annotation_);
|
||||
}
|
||||
|
||||
void LogsWindow::on_autoScroll_toggled(bool checked)
|
||||
{
|
||||
if (checked) {
|
||||
connect(logs->model(),
|
||||
SIGNAL(rowsInserted(const QModelIndex&, int, int)),
|
||||
logs, SLOT(scrollToBottom()));
|
||||
}
|
||||
else {
|
||||
disconnect(logs->model(),
|
||||
SIGNAL(rowsInserted(const QModelIndex&, int, int)),
|
||||
logs, SLOT(scrollToBottom()));
|
||||
}
|
||||
}
|
||||
|
@ -23,6 +23,8 @@ along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
#include "ui_logswindow.h"
|
||||
|
||||
class LogsModel;
|
||||
class QDockWidget;
|
||||
class QShowEvent;
|
||||
|
||||
class LogsWindow: public QWidget, private Ui::LogsWindow
|
||||
{
|
||||
@ -30,6 +32,18 @@ class LogsWindow: public QWidget, private Ui::LogsWindow
|
||||
public:
|
||||
LogsWindow(LogsModel *model, QWidget *parent = 0);
|
||||
~LogsWindow();
|
||||
|
||||
private slots:
|
||||
void when_visibilityChanged(bool visible);
|
||||
void when_rowsInserted(const QModelIndex &parent, int first, int last);
|
||||
void on_autoScroll_toggled(bool checked);
|
||||
|
||||
private:
|
||||
QDockWidget *parentDock_;
|
||||
QString windowTitle_;
|
||||
QString annotation_;
|
||||
bool isVisible_{false};
|
||||
QObject *logsModelTest_;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
@ -41,6 +41,35 @@
|
||||
<property name="statusTip">
|
||||
<string>Select the desired logging level</string>
|
||||
</property>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Info</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Warning</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Error</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="Line" name="line">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="autoScroll">
|
||||
<property name="text">
|
||||
<string>Auto Scroll</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
@ -81,6 +110,9 @@
|
||||
<property name="whatsThis">
|
||||
<string>Ostinato application logs are displayed here</string>
|
||||
</property>
|
||||
<property name="selectionBehavior">
|
||||
<enum>QAbstractItemView::SelectRows</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
@ -94,6 +126,7 @@
|
||||
</customwidgets>
|
||||
<tabstops>
|
||||
<tabstop>level</tabstop>
|
||||
<tabstop>autoScroll</tabstop>
|
||||
<tabstop>clear</tabstop>
|
||||
<tabstop>logs</tabstop>
|
||||
</tabstops>
|
||||
|
@ -96,7 +96,6 @@ MainWindow::MainWindow(QWidget *parent)
|
||||
|
||||
portsWindow = new PortsWindow(pgl, this);
|
||||
statsWindow = new PortStatsWindow(pgl, this);
|
||||
logsWindow_ = new LogsWindow(appLogs, this);
|
||||
|
||||
portsDock = new QDockWidget(tr("Ports and Streams"), this);
|
||||
portsDock->setObjectName("portsDock");
|
||||
@ -112,6 +111,7 @@ MainWindow::MainWindow(QWidget *parent)
|
||||
logsDock_->setObjectName("logsDock");
|
||||
logsDock_->setFeatures(
|
||||
logsDock_->features() & ~QDockWidget::DockWidgetClosable);
|
||||
logsWindow_ = new LogsWindow(appLogs, logsDock_);
|
||||
|
||||
setupUi(this);
|
||||
|
||||
|
@ -22,6 +22,8 @@ along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
|
||||
#include <QTableView>
|
||||
|
||||
#include <QClipboard>
|
||||
#include <QKeyEvent>
|
||||
#include <QPainter>
|
||||
|
||||
class XTableView : public QTableView
|
||||
@ -42,6 +44,31 @@ protected:
|
||||
else
|
||||
QTableView::paintEvent(event);
|
||||
}
|
||||
|
||||
virtual void keyPressEvent(QKeyEvent *event)
|
||||
{
|
||||
// Copy selection to clipboard (base class copies only current item)
|
||||
if (event->matches(QKeySequence::Copy)
|
||||
&& selectionBehavior() == SelectRows) {
|
||||
QString text;
|
||||
int lastRow = -1;
|
||||
QModelIndexList selected = selectionModel()->selectedIndexes();
|
||||
qSort(selected);
|
||||
foreach(QModelIndex index, selected) {
|
||||
if (index.row() != lastRow) {
|
||||
if (!text.isEmpty())
|
||||
text.append("\n");
|
||||
}
|
||||
else
|
||||
text.append("\t");
|
||||
text.append(model()->data(index).toString());
|
||||
lastRow = index.row();
|
||||
}
|
||||
qApp->clipboard()->setText(text);
|
||||
}
|
||||
else
|
||||
QTableView::keyPressEvent(event);
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
||||
|
Loading…
Reference in New Issue
Block a user