Qt: Improve UIX for sparklines

Sparklines should display to the user, which interfaces are active
and ready for capture. Additionally it should be easy to find active
interfaces, without filtering first.

This change reorders the interface list, in order to sort active
interfaces on top, as well as hide information if no packet has been
received on that interface, to ensure that the user can find active
interfaces faster, making it easier to capture on systems where
the interfaces have very generic names.

The interface context menu has been amended to allow interfaces to be
hidden/unhidden from the main interface list as well
This commit is contained in:
Roland Knall 2022-04-21 16:00:50 +02:00
parent 49ec7da90e
commit 1278af07c3
7 changed files with 59 additions and 5 deletions

View File

@ -109,6 +109,9 @@ They previously shipped with Npcap 1.55.
* ZigBee ZCL Messaging: rename zbee_zcl_se.msg.msg_ctrl.depreciated to zbee_zcl_se.msg.msg_ctrl.deprecated
* The interface list on the welcome page sorts active interfaces first and only displays the sparkline for active interfaces.
Additionally, the interfaces can now be hidden/unhidden via the context menu in the interface list
=== Removed Features and Support
* CMake: The options starting with DISABLE_something were renamed ENABLE_something for consistency.

View File

@ -793,7 +793,8 @@ void CaptureOptionsDialog::updateInterfaces()
ti->setText(col_interface_, device->display_name);
ti->setData(col_interface_, Qt::UserRole, QString(device->name));
ti->setData(col_traffic_, Qt::UserRole, QVariant::fromValue(ti->points));
if (device->if_info.type != IF_EXTCAP)
ti->setData(col_traffic_, Qt::UserRole, QVariant::fromValue(ti->points));
if (device->no_addresses > 0) {
QString addr_str = tr("%1: %2").arg(device->no_addresses > 1 ? tr("Addresses") : tr("Address")).arg(device->addresses);

View File

@ -30,6 +30,8 @@
#include "extcap.h"
#include <ui/recent.h>
#include "capture_opts.h"
#include "ui/capture_globals.h"
#include <wsutil/utf8_entities.h>
#include <QDesktopServices>
@ -100,6 +102,7 @@ InterfaceFrame::InterfaceFrame(QWidget * parent)
info_model_.setColumn(static_cast<int>(columns.indexOf(IFTREE_COL_STATS)));
ui->interfaceTree->setModel(&info_model_);
ui->interfaceTree->setSortingEnabled(true);
ui->interfaceTree->setItemDelegateForColumn(proxy_model_.mapSourceToColumn(IFTREE_COL_STATS), new SparkLineDelegate(this));
@ -247,6 +250,7 @@ void InterfaceFrame::interfaceListChanged()
void InterfaceFrame::toggleHiddenInterfaces()
{
source_model_.interfaceListChanged();
proxy_model_.toggleFilterHidden();
emit typeSelectionChanged();
@ -475,7 +479,6 @@ void InterfaceFrame::updateStatistics(void)
if (selectIndex.isValid())
source_model_.updateStatistic(idx);
}
#endif
}
@ -509,6 +512,21 @@ void InterfaceFrame::showContextMenu(QPoint pos)
startCapture(ifaces);
});
ctx_menu.addSeparator();
QModelIndex actIndex = ui->interfaceTree->indexAt(pos);
QModelIndex realIndex = proxy_model_.mapToSource(info_model_.mapToSource(actIndex));
bool isHidden = realIndex.sibling(realIndex.row(), IFTREE_COL_HIDDEN).data(Qt::UserRole).toBool();
QAction * hideAction = ctx_menu.addAction(tr("Hide Interface"), this, [=] () {
/* Attention! Only realIndex.row is a 1:1 correlation to all_ifaces */
interface_t *device = &g_array_index(global_capture_opts.all_ifaces, interface_t, realIndex.row());
device->hidden = ! device->hidden;
mainApp->emitAppSignal(MainApplication::LocalInterfacesChanged);
});
hideAction->setCheckable(true);
hideAction->setChecked(isHidden);
ctx_menu.exec(ui->interfaceTree->mapToGlobal(pos));
}

View File

@ -369,3 +369,15 @@ QString InterfaceSortFilterModel::interfaceError()
return result;
}
bool InterfaceSortFilterModel::lessThan(const QModelIndex &source_left, const QModelIndex &source_right) const
{
bool leftActive = source_left.sibling(source_left.row(), InterfaceTreeColumns::IFTREE_COL_ACTIVE).data(Qt::UserRole).toBool();
bool rightActive = source_right.sibling(source_right.row(), InterfaceTreeColumns::IFTREE_COL_ACTIVE).data(Qt::UserRole).toBool();
if (rightActive && ! leftActive)
return true;
return QSortFilterProxyModel::lessThan(source_left, source_right);
}

View File

@ -60,6 +60,7 @@ public:
protected:
bool filterAcceptsRow(int source_row, const QModelIndex & source_parent) const;
bool filterAcceptsColumn(int source_column, const QModelIndex & source_parent) const;
bool lessThan(const QModelIndex &source_left, const QModelIndex &source_right) const;
private:
bool _filterHidden;

View File

@ -227,9 +227,14 @@ QVariant InterfaceTreeModel::data(const QModelIndex &index, int role) const
{
if (col == IFTREE_COL_STATS)
{
if (points.contains(device->name))
if ((active.contains(device->name) && active[device->name]) && points.contains(device->name))
return QVariant::fromValue(points[device->name]);
}
else if (col == IFTREE_COL_ACTIVE)
{
if (active.contains(device->name))
return QVariant::fromValue(active[device->name]);
}
else if (col == IFTREE_COL_HIDDEN)
{
return QVariant::fromValue((bool)device->hidden);
@ -354,6 +359,7 @@ void InterfaceTreeModel::interfaceListChanged()
emit beginResetModel();
points.clear();
active.clear();
emit endResetModel();
}
@ -439,9 +445,13 @@ void InterfaceTreeModel::updateStatistic(unsigned int idx)
struct pcap_stat stats;
unsigned diff = 0;
bool isActive = false;
if (capture_stats(stat_cache_, device->name, &stats))
{
if ( (int) stats.ps_recv > 0 )
isActive = true;
if ((int)(stats.ps_recv - device->last_packets) >= 0)
{
diff = stats.ps_recv - device->last_packets;
@ -451,7 +461,16 @@ void InterfaceTreeModel::updateStatistic(unsigned int idx)
}
points[device->name].append(diff);
if (active[device->name] != isActive)
{
emit beginResetModel();
active[device->name] = isActive;
emit endResetModel();
}
emit dataChanged(index(idx, IFTREE_COL_STATS), index(idx, IFTREE_COL_STATS));
#else
Q_UNUSED(idx)
#endif

View File

@ -41,6 +41,7 @@ enum InterfaceTreeColumns
IFTREE_COL_PROMISCUOUSMODE,
IFTREE_COL_TYPE,
IFTREE_COL_STATS,
IFTREE_COL_ACTIVE,
IFTREE_COL_SNAPLEN,
#ifdef CAN_SET_CAPTURE_BUFFER_SIZE
IFTREE_COL_BUFFERLEN,
@ -85,13 +86,12 @@ public:
public slots:
void getPoints(int idx, PointList *pts);
protected slots:
void interfaceListChanged();
private:
QVariant toolTipForInterface(int idx) const;
QMap<QString, PointList> points;
QMap<QString, bool> active;
#ifdef HAVE_LIBPCAP
if_stat_cache_t *stat_cache_;