Replace Port Stats Link/Tx/Capture state text with icons

* All 3 icons are combined into a single row instead of 3 separate rows
* Tooltip to clarify meaning of icons
* Qt Model-View displays icon left-aligned, so use a custom delegate to
  center-align it
* Add a icon for "Transmit On"
* Edit icons for "Start/Stop Capture"
This commit is contained in:
Srivats P 2018-11-29 20:44:03 +05:30
parent 48721cece4
commit 496e044bdd
9 changed files with 193 additions and 73 deletions

40
client/icononlydelegate.h Normal file
View File

@ -0,0 +1,40 @@
/*
Copyright (C) 2018 Srivats P.
This file is part of "Ostinato"
This is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>
*/
#ifndef _ICON_ONLY_DELEGATE
#define _ICON_ONLY_DELEGATE
#include <QStyledItemDelegate>
class IconOnlyDelegate : public QStyledItemDelegate
{
using QStyledItemDelegate::QStyledItemDelegate;
void paint(QPainter *painter, const QStyleOptionViewItem &option,
const QModelIndex &index) const
{
QStyleOptionViewItem opt = option;
opt.decorationPosition = QStyleOptionViewItem::Top;
opt.features.setFlag(QStyleOptionViewItem::HasDisplay, false);
QStyledItemDelegate::paint(painter, opt, index);
}
};
#endif

Binary file not shown.

Before

Width:  |  Height:  |  Size: 474 B

After

Width:  |  Height:  |  Size: 514 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 417 B

After

Width:  |  Height:  |  Size: 435 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 749 B

View File

@ -1,48 +1,49 @@
<RCC>
<qresource prefix="/" >
<file>icons/about.png</file>
<file>icons/add.png</file>
<file>icons/arrow_down.png</file>
<file>icons/arrow_left.png</file>
<file>icons/arrow_right.png</file>
<file>icons/arrow_up.png</file>
<file>icons/bullet_error.png</file>
<file>icons/bullet_green.png</file>
<file>icons/bullet_orange.png</file>
<file>icons/bullet_red.png</file>
<file>icons/bullet_white.png</file>
<file>icons/bullet_yellow.png</file>
<file>icons/control_play.png</file>
<file>icons/control_stop.png</file>
<file>icons/deco_exclusive.png</file>
<file>icons/delete.png</file>
<file>icons/devicegroup_add.png</file>
<file>icons/devicegroup_delete.png</file>
<file>icons/devicegroup_edit.png</file>
<file>icons/exit.png</file>
<file>icons/gaps.png</file>
<file>icons/help.png</file>
<file>icons/logo.png</file>
<file>icons/magnifier.png</file>
<file>icons/name.png</file>
<file>icons/neighbor_clear.png</file>
<file>icons/neighbor_resolve.png</file>
<file>icons/portgroup_add.png</file>
<file>icons/portgroup_connect.png</file>
<file>icons/portgroup_delete.png</file>
<file>icons/portgroup_disconnect.png</file>
<file>icons/portstats_clear.png</file>
<file>icons/portstats_clear_all.png</file>
<file>icons/portstats_filter.png</file>
<file>icons/preferences.png</file>
<file>icons/qt.png</file>
<file>icons/refresh.png</file>
<file>icons/sound_mute.png</file>
<file>icons/sound_none.png</file>
<file>icons/stream_add.png</file>
<file>icons/stream_delete.png</file>
<file>icons/stream_duplicate.png</file>
<file>icons/stream_edit.png</file>
<file>icons/stream_stats.png</file>
</qresource>
<qresource prefix="/">
<file>icons/transmit_on.png</file>
<file>icons/about.png</file>
<file>icons/add.png</file>
<file>icons/arrow_down.png</file>
<file>icons/arrow_left.png</file>
<file>icons/arrow_right.png</file>
<file>icons/arrow_up.png</file>
<file>icons/bullet_error.png</file>
<file>icons/bullet_green.png</file>
<file>icons/bullet_orange.png</file>
<file>icons/bullet_red.png</file>
<file>icons/bullet_white.png</file>
<file>icons/bullet_yellow.png</file>
<file>icons/control_play.png</file>
<file>icons/control_stop.png</file>
<file>icons/deco_exclusive.png</file>
<file>icons/delete.png</file>
<file>icons/devicegroup_add.png</file>
<file>icons/devicegroup_delete.png</file>
<file>icons/devicegroup_edit.png</file>
<file>icons/exit.png</file>
<file>icons/gaps.png</file>
<file>icons/help.png</file>
<file>icons/logo.png</file>
<file>icons/magnifier.png</file>
<file>icons/name.png</file>
<file>icons/neighbor_clear.png</file>
<file>icons/neighbor_resolve.png</file>
<file>icons/portgroup_add.png</file>
<file>icons/portgroup_connect.png</file>
<file>icons/portgroup_delete.png</file>
<file>icons/portgroup_disconnect.png</file>
<file>icons/portstats_clear.png</file>
<file>icons/portstats_clear_all.png</file>
<file>icons/portstats_filter.png</file>
<file>icons/preferences.png</file>
<file>icons/qt.png</file>
<file>icons/refresh.png</file>
<file>icons/sound_mute.png</file>
<file>icons/sound_none.png</file>
<file>icons/stream_add.png</file>
<file>icons/stream_delete.png</file>
<file>icons/stream_duplicate.png</file>
<file>icons/stream_edit.png</file>
<file>icons/stream_stats.png</file>
</qresource>
</RCC>

View File

@ -20,6 +20,8 @@ along with this program. If not, see <http://www.gnu.org/licenses/>
#include "portstatsmodel.h"
#include "portgrouplist.h"
#include <QPainter>
#include <QPixmapCache>
#include <QTimer>
enum {
@ -94,15 +96,12 @@ void PortStatsModel::getDomainIndexes(const QModelIndex &index,
QVariant PortStatsModel::data(const QModelIndex &index, int role) const
{
uint pgidx, pidx;
int row;
// Check for a valid index
if (!index.isValid())
return QVariant();
// Check for row/column limits
row = index.row();
int row = index.row();
if (row >= e_STAT_MAX)
return QVariant();
@ -112,15 +111,22 @@ QVariant PortStatsModel::data(const QModelIndex &index, int role) const
if (index.column() >= (numPorts.last()))
return QVariant();
if (role == Qt::TextAlignmentRole)
{
if (row >= e_STATISTICS_START && row <= e_STATISTICS_END)
return Qt::AlignRight; // right-align numbers
else
return Qt::AlignHCenter; // center-align everything else
}
uint pgidx, pidx;
getDomainIndexes(index, pgidx, pidx);
OstProto::PortStats stats = pgl->mPortGroups.at(pgidx)
->mPorts[pidx]->getStats();
// Check role
if (role == Qt::DisplayRole)
{
OstProto::PortStats stats;
stats = pgl->mPortGroups.at(pgidx)->mPorts[pidx]->getStats();
switch(row)
{
// Info
@ -128,14 +134,8 @@ QVariant PortStatsModel::data(const QModelIndex &index, int role) const
return pgl->mPortGroups.at(pgidx)->mPorts[pidx]->userName();
// States
case e_LINK_STATE:
return LinkStateName.at(stats.state().link_state());
case e_TRANSMIT_STATE:
return BoolStateName.at(stats.state().is_transmit_on());
case e_CAPTURE_STATE:
return BoolStateName.at(stats.state().is_capture_on());
case e_COMBO_STATE:
return QVariant();
// Statistics
case e_STAT_FRAMES_RCVD:
@ -201,12 +201,26 @@ QVariant PortStatsModel::data(const QModelIndex &index, int role) const
return 0;
}
}
else if (role == Qt::TextAlignmentRole)
else if (role == Qt::DecorationRole)
{
if (row >= e_STATISTICS_START && row <= e_STATISTICS_END)
return Qt::AlignRight; // right-align numbers
if (row == e_COMBO_STATE)
return statusIcons(
stats.state().link_state(),
stats.state().is_transmit_on(),
stats.state().is_capture_on());
else
return Qt::AlignHCenter; // center-align everything else
return QVariant();
}
else if (role == Qt::ToolTipRole)
{
if (row == e_COMBO_STATE)
return QString("Transmit:<b><i>%1</i></b> Link:<b><i>%2</i></b> "
"Capture:<b><i>%3</i></b>")
.arg(BoolStateName.at(stats.state().is_transmit_on()))
.arg(LinkStateName.at(stats.state().link_state()))
.arg(BoolStateName.at(stats.state().is_capture_on()));
else
return QVariant();
}
else
return QVariant();
@ -351,3 +365,49 @@ void PortStatsModel::when_portGroup_stats_update(quint32 /*portGroupId*/)
emit dataChanged(topLeft, bottomRight);
}
QPixmap PortStatsModel::statusIcons(
int linkState, bool transmit, bool capture) const
{
QPixmap pixmap;
QString key = QString("$ost:statusList:%1:%2:%3")
.arg(linkState).arg(transmit).arg(capture);
if (QPixmapCache::find(key, pixmap))
return pixmap;
static int sz = QPixmap(":/icons/transmit_on.png").width();
// Assume all icons are of same size and are square
QPixmap blank(sz, sz);
blank.fill(Qt::transparent);
pixmap = QPixmap(sz*3, sz);
pixmap.fill(Qt::transparent);
QPainter painter(&pixmap);
painter.drawPixmap(0, 0,
transmit ? QPixmap(":/icons/transmit_on.png") : blank);
switch (linkState) {
case OstProto::LinkStateUp:
painter.drawPixmap(sz, 0, QPixmap(":/icons/bullet_green.png"));
break;
case OstProto::LinkStateDown:
painter.drawPixmap(sz, 0, QPixmap(":/icons/bullet_red.png"));
break;
case OstProto::LinkStateUnknown:
painter.drawPixmap(sz, 0, QPixmap(":/icons/bullet_white.png"));
break;
default:
painter.drawPixmap(sz, 0, blank);
}
painter.drawPixmap(sz*2, 0,
capture ? QPixmap(":/icons/sound_none.png") : blank);
QPixmapCache::insert(key, pixmap);
return pixmap;
}

View File

@ -36,11 +36,9 @@ typedef enum {
// State
e_STATE_START,
e_LINK_STATE = e_STATE_START,
e_TRANSMIT_STATE,
e_CAPTURE_STATE,
e_COMBO_STATE = e_STATE_START,
e_STATE_END = e_CAPTURE_STATE,
e_STATE_END = e_COMBO_STATE,
// Statistics
e_STATISTICS_START,
@ -77,9 +75,7 @@ typedef enum {
static QStringList PortStatName = (QStringList()
<< "User"
<< "Link State"
<< "Transmit State"
<< "Capture State"
<< "Status"
<< "Frames Received"
<< "Frames Sent"
@ -160,6 +156,7 @@ class PortStatsModel : public QAbstractTableModel
void getDomainIndexes(const QModelIndex &index,
uint &portGroupIdx, uint &portIdx) const;
QPixmap statusIcons(int linkState, bool transmit, bool capture) const;
};

View File

@ -20,6 +20,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>
#include "portstatswindow.h"
#include "icononlydelegate.h"
#include "portstatsfilterdialog.h"
#include "portstatsmodel.h"
#include "portstatsproxymodel.h"
@ -53,6 +54,24 @@ PortStatsWindow::PortStatsWindow(PortGroupList *pgl, QWidget *parent)
tvPortStats->verticalHeader()->setHighlightSections(false);
tvPortStats->verticalHeader()->setDefaultSectionSize(
tvPortStats->verticalHeader()->minimumSectionSize());
statusDelegate = new IconOnlyDelegate(this);
#if 0
// XXX: Ideally we should use this, but it doesn't work because in
// this constructor, the port model is empty and model->index() returns
// an invalid index ...
tvPortStats->setItemDelegateForRow(
proxyStatsModel ?
proxyStatsModel->mapFromSource(model->index(e_COMBO_STATE, 0))
.row() :
e_COMBO_STATE,
statusDelegate);
#else
// ... so we use this hard-coded hack
tvPortStats->setItemDelegateForRow(
proxyStatsModel ? e_COMBO_STATE-1 : e_COMBO_STATE,
statusDelegate);
#endif
connect(tvPortStats->selectionModel(),
SIGNAL(selectionChanged(
@ -65,6 +84,7 @@ PortStatsWindow::PortStatsWindow(PortGroupList *pgl, QWidget *parent)
PortStatsWindow::~PortStatsWindow()
{
delete proxyStatsModel;
delete statusDelegate;
}
/* ------------- SLOTS (public) -------------- */

View File

@ -27,6 +27,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>
#include "portstatsmodel.h"
class QSortFilterProxyModel;
class QStyledItemDelegate;
class PortStatsWindow : public QWidget, public Ui::PortStatsWindow
{
@ -63,6 +64,7 @@ private:
PortGroupList *pgl;
PortStatsModel *model;
QSortFilterProxyModel *proxyStatsModel;
QStyledItemDelegate *statusDelegate;
QModelIndexList selectedColumns;
};