Qt: Add "Copy As..." to the Protocol Hierarchy stats.

This implementation is slightly different compared to other dialogs
since we have nested items.

Tweak the bar highlight color look better on Windows while we're here.

Change-Id: If0607c4624f304042fe3d6c8a941756b342e703d
Reviewed-on: https://code.wireshark.org/review/6953
Petri-Dish: Gerald Combs <gerald@wireshark.org>
Tested-by: Petri Dish Buildbot <buildbot-no-reply@wireshark.org>
Reviewed-by: Gerald Combs <gerald@wireshark.org>
This commit is contained in:
Gerald Combs 2015-02-04 08:53:44 -08:00
parent a8cc3318b1
commit a748e83143
3 changed files with 144 additions and 10 deletions

View File

@ -29,10 +29,11 @@
#include "qt_ui_utils.h"
#include "wireshark_application.h"
#include <QRect>
#include <QClipboard>
#include <QPainter>
#include <QTreeWidget>
#include <QTreeWidgetItem>
#include <QPushButton>
#include <QTextStream>
#include <QTreeWidgetItemIterator>
/*
* @file Protocol Hierarchy Statistics dialog
@ -42,7 +43,7 @@
*/
// To do:
// - Copy as... YAML?
// - Make "Copy as YAML" output a tree?
// - Add time series data to ph_stats_node_t and draw sparklines.
const int protocol_col_ = 0;
@ -55,8 +56,6 @@ const int end_packets_col_ = 6;
const int end_bytes_col_ = 7;
const int end_bandwidth_col_ = 8;
#include <QDebug>
const int bar_em_width_ = 8;
const double bar_blend_ = 0.15;
void PercentBarDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option,
@ -85,7 +84,7 @@ void PercentBarDelegate::paint(QPainter *painter, const QStyleOptionViewItem &op
cg = QPalette::Inactive;
if (optv4.state & QStyle::State_Selected) {
painter->setPen(optv4.palette.color(cg, QPalette::HighlightedText));
bar_color = ColorUtils::alphaBlend(optv4.palette.color(cg, QPalette::HighlightedText),
bar_color = ColorUtils::alphaBlend(optv4.palette.color(cg, QPalette::Window),
optv4.palette.color(cg, QPalette::Highlight),
bar_blend_);
} else {
@ -144,15 +143,42 @@ public:
setText(protocol_col_, ph_stats_node->hfinfo->name);
setData(pct_packets_col_, Qt::UserRole, percent_packets_);
setText(packets_col_, QString::number(ph_stats_node->num_pkts_total));
setText(packets_col_, QString::number(total_packets_));
setData(pct_bytes_col_, Qt::UserRole, percent_bytes_);
setText(bytes_col_, QString::number(ph_stats_node->num_bytes_total));
setText(bytes_col_, QString::number(total_bytes_));
setText(bandwidth_col_, seconds > 0.0 ? bits_s_to_qstring(bits_s_) : UTF8_EM_DASH);
setText(end_packets_col_, QString::number(last_packets_));
setText(end_bytes_col_, QString::number(last_bytes_));
setText(end_bandwidth_col_, seconds > 0.0 ? bits_s_to_qstring(end_bits_s_) : UTF8_EM_DASH);
}
// Return a QString, int, double, or invalid QVariant representing the raw column data.
QVariant colData(int col) const {
switch(col) {
case protocol_col_:
return text(col);
case (pct_packets_col_):
return percent_packets_;
case (packets_col_):
return total_packets_;
case (pct_bytes_col_):
return percent_bytes_;
case (bytes_col_):
return total_bytes_;
case (bandwidth_col_):
return bits_s_;
case (end_packets_col_):
return last_packets_;
case (end_bytes_col_):
return last_bytes_;
case (end_bandwidth_col_):
return end_bits_s_;
default:
break;
}
return QVariant();
}
bool operator< (const QTreeWidgetItem &other) const
{
const ProtocolHierarchyTreeWidgetItem &other_phtwi = dynamic_cast<const ProtocolHierarchyTreeWidgetItem&>(other);
@ -252,6 +278,23 @@ ProtocolHierarchyDialog::ProtocolHierarchyDialog(QWidget &parent, CaptureFile &c
fa = new FilterAction(&ctx_menu_, FilterAction::ActionColorize);
ctx_menu_.addAction(fa);
ctx_menu_.addSeparator();
ctx_menu_.addAction(ui->actionCopyAsCsv);
ctx_menu_.addAction(ui->actionCopyAsYaml);
copy_button_ = ui->buttonBox->addButton(tr("Copy"), QDialogButtonBox::ApplyRole);
QMenu *copy_menu = new QMenu();
QAction *ca;
ca = copy_menu->addAction(tr("as CSV"));
ca->setToolTip(ui->actionCopyAsCsv->toolTip());
connect(ca, SIGNAL(triggered()), this, SLOT(on_actionCopyAsCsv_triggered()));
ca = copy_menu->addAction(tr("as YAML"));
ca->setToolTip(ui->actionCopyAsYaml->toolTip());
connect(ca, SIGNAL(triggered()), this, SLOT(on_actionCopyAsYaml_triggered()));
copy_button_->setMenu(copy_menu);
display_filter_ = cap_file_.capFile()->dfilter;
updateWidgets();
}
@ -269,7 +312,9 @@ void ProtocolHierarchyDialog::showProtoHierMenu(QPoint pos)
submenu->setEnabled(enable);
}
foreach (QAction *action, ctx_menu_.actions()) {
action->setEnabled(enable);
if (action != ui->actionCopyAsCsv && action != ui->actionCopyAsYaml) {
action->setEnabled(enable);
}
}
ctx_menu_.popup(ui->hierStatsTreeWidget->viewport()->mapToGlobal(pos));
@ -314,6 +359,72 @@ void ProtocolHierarchyDialog::updateWidgets()
ui->hintLabel->setText(hint);
}
QList<QVariant> ProtocolHierarchyDialog::protoHierRowData(QTreeWidgetItem *item) const
{
QList<QVariant> row_data;
for (int col = 0; col < ui->hierStatsTreeWidget->columnCount(); col++) {
if (!item) {
row_data << ui->hierStatsTreeWidget->headerItem()->text(col);
} else {
ProtocolHierarchyTreeWidgetItem *phti = static_cast<ProtocolHierarchyTreeWidgetItem*>(item);
if (phti) {
row_data << phti->colData(col);
}
}
}
return row_data;
}
void ProtocolHierarchyDialog::on_actionCopyAsCsv_triggered()
{
QString csv;
QTextStream stream(&csv, QIODevice::Text);
QTreeWidgetItemIterator iter(ui->hierStatsTreeWidget);
bool first = true;
while (*iter) {
QStringList separated_value;
QTreeWidgetItem *item = first ? NULL : (*iter);
foreach (QVariant v, protoHierRowData(item)) {
if (!v.isValid()) {
separated_value << "\"\"";
} else if ((int) v.type() == (int) QMetaType::QString) {
separated_value << QString("\"%1\"").arg(v.toString());
} else {
separated_value << v.toString();
}
}
stream << separated_value.join(",") << endl;
if (!first) iter++;
first = false;
}
wsApp->clipboard()->setText(stream.readAll());
}
void ProtocolHierarchyDialog::on_actionCopyAsYaml_triggered()
{
QString yaml;
QTextStream stream(&yaml, QIODevice::Text);
QTreeWidgetItemIterator iter(ui->hierStatsTreeWidget);
bool first = true;
stream << "---" << endl;
while (*iter) {
QTreeWidgetItem *item = first ? NULL : (*iter);
stream << "-" << endl;
foreach (QVariant v, protoHierRowData(item)) {
stream << " - " << v.toString() << endl;
}
if (!first) iter++;
first = false;
}
wsApp->clipboard()->setText(stream.readAll());
}
void ProtocolHierarchyDialog::on_buttonBox_helpRequested()
{
wsApp->helpTopicAction(HELP_STATS_PROTO_HIERARCHY_DIALOG);

View File

@ -27,6 +27,9 @@
#include <QMenu>
class QPushButton;
class QTreeWidgetItem;
namespace Ui {
class ProtocolHierarchyDialog;
}
@ -63,10 +66,13 @@ signals:
private slots:
void showProtoHierMenu(QPoint pos);
void filterActionTriggered();
void on_actionCopyAsCsv_triggered();
void on_actionCopyAsYaml_triggered();
void on_buttonBox_helpRequested();
private:
Ui::ProtocolHierarchyDialog *ui;
QPushButton *copy_button_;
QMenu ctx_menu_;
PercentBarDelegate percent_bar_delegate_;
QString display_filter_;
@ -74,6 +80,7 @@ private:
// Callback for g_node_children_foreach
static void addTreeNode(GNode *node, gpointer data);
void updateWidgets();
QList<QVariant> protoHierRowData(QTreeWidgetItem *item) const;
};
#endif // PROTOCOL_HIERARCHY_DIALOG_H

View File

@ -87,6 +87,22 @@
</widget>
</item>
</layout>
<action name="actionCopyAsCsv">
<property name="text">
<string>Copy as CSV</string>
</property>
<property name="toolTip">
<string>Copy stream list as CSV.</string>
</property>
</action>
<action name="actionCopyAsYaml">
<property name="text">
<string>Copy as YAML</string>
</property>
<property name="toolTip">
<string>Copy stream list as YAML.</string>
</property>
</action>
</widget>
<resources/>
<connections>