Qt: Add Time and Comment labels to the sequence diagram.

Add "Time" and "Comment" labels to the sequence diagram similar to the
GTK+ UI. Draw a border around the diagram as well.

Widen the default spacing and set it to a simple em-width multiple.

Fix our port number alignment.

Copy over the sequence diagram colors from the GTK+ UI and add them to
ColorUtils. Color sequences according to their respective conversation
numbers.

To do:
- Add zoom.

Ping-Bug: 12419
Change-Id: I3f9b4ffbfcc34aae1c38e303cd36ff207be247b1
Reviewed-on: https://code.wireshark.org/review/15554
Reviewed-by: Gerald Combs <gerald@wireshark.org>
Petri-Dish: Gerald Combs <gerald@wireshark.org>
Tested-by: Petri Dish Buildbot <buildbot-no-reply@wireshark.org>
Reviewed-by: Anders Broman <a.broman58@gmail.com>
This commit is contained in:
Gerald Combs 2016-05-19 11:21:02 +09:00 committed by Anders Broman
parent bf628988b6
commit 6952c1342a
5 changed files with 159 additions and 77 deletions

View File

@ -31,24 +31,6 @@ const QColor ColorUtils::expert_color_error = QColor ( 0xff, 0x5c, 0x5c );
const QColor ColorUtils::expert_color_foreground = QColor ( 0x00, 0x00, 0x00 ); /* Black */
const QColor ColorUtils::hidden_proto_item = QColor ( 0x44, 0x44, 0x44 ); /* Gray */
// Available colors
// XXX - Add custom
const QList<QRgb> ColorUtils::graph_colors_ = QList<QRgb>()
<< tango_aluminium_6 // Bar outline (use black instead)?
<< tango_sky_blue_5
<< tango_butter_6
<< tango_chameleon_5
<< tango_scarlet_red_5
<< tango_plum_5
<< tango_orange_6
<< tango_aluminium_3
<< tango_sky_blue_3
<< tango_butter_3
<< tango_chameleon_3
<< tango_scarlet_red_3
<< tango_plum_3
<< tango_orange_3;
ColorUtils::ColorUtils(QObject *parent) :
QObject(parent)
{
@ -109,6 +91,58 @@ QRgb ColorUtils::alphaBlend(const QBrush &brush1, const QBrush &brush2, qreal al
return alphaBlend(brush1.color(), brush2.color(), alpha);
}
QList<QRgb> ColorUtils::graph_colors_;
const QList<QRgb> ColorUtils::graphColors()
{
if (graph_colors_.isEmpty()) {
// Available graph colors
// XXX - Add custom
graph_colors_ = QList<QRgb>()
<< tango_aluminium_6 // Bar outline (use black instead)?
<< tango_sky_blue_5
<< tango_butter_6
<< tango_chameleon_5
<< tango_scarlet_red_5
<< tango_plum_5
<< tango_orange_6
<< tango_aluminium_3
<< tango_sky_blue_3
<< tango_butter_3
<< tango_chameleon_3
<< tango_scarlet_red_3
<< tango_plum_3
<< tango_orange_3;
}
return graph_colors_;
}
QRgb ColorUtils::graphColor(int item)
{
if (graph_colors_.isEmpty()) graphColors(); // Init list.
return graph_colors_[item % graph_colors_.size()];
}
QList<QRgb> ColorUtils::sequence_colors_;
QRgb ColorUtils::sequenceColor(int item)
{
if (sequence_colors_.isEmpty()) {
// Available sequence colors. Copied from gtk/graph_analysis.c.
// XXX - Add custom?
sequence_colors_ = QList<QRgb>()
<< qRgb(144, 238, 144)
<< qRgb(255, 160, 123)
<< qRgb(255, 182, 193)
<< qRgb(250, 250, 210)
<< qRgb(255, 255, 52)
<< qRgb(103, 205, 170)
<< qRgb(224, 255, 255)
<< qRgb(176, 196, 222)
<< qRgb(135, 206, 254)
<< qRgb(211, 211, 211);
}
return sequence_colors_[item % sequence_colors_.size()];
}
/*
* Editor modelines
*

View File

@ -53,15 +53,17 @@ public:
static const QColor expert_color_foreground; /* black */
static const QColor hidden_proto_item; /* gray */
static const QList<QRgb> graphColors() { return graph_colors_; }
static QRgb graphColor(int item) { return graph_colors_[item % graph_colors_.size()]; }
static const QList<QRgb> graphColors();
static QRgb graphColor(int item);
static QRgb sequenceColor(int item);
signals:
public slots:
private:
static const QList<QRgb> graph_colors_;
static QList<QRgb> graph_colors_;
static QList<QRgb> sequence_colors_;
};
void color_filter_qt_add_cb(color_filter_t *colorf, gpointer user_data);

View File

@ -25,6 +25,7 @@
#include "ui/tap-sequence-analysis.h"
#include "color_utils.h"
#include "qt_ui_utils.h"
#include <QFont>
@ -187,7 +188,7 @@ void SequenceDiagram::draw(QCPPainter *painter)
QPen fg_pen;
qreal alpha = 0.50;
// Lifelines (node lines)
// Lifelines (node lines). Will likely be overdrawn below.
painter->save();
painter->setOpacity(alpha);
fg_pen = mainPen();
@ -208,36 +209,41 @@ void SequenceDiagram::draw(QCPPainter *painter)
double cur_key = it.key();
seq_analysis_item_t *sai = it.value().value;
QPen fg_pen(mainPen());
QColor bg_color;
if (sai->frame_number == selected_packet_) {
// Highlighted background
painter->save();
QRect bg_rect(
QPoint(coordsToPixels(cur_key - 0.5, value_axis_->range().lower).toPoint()),
QPoint(coordsToPixels(cur_key + 0.5, value_axis_->range().upper).toPoint()));
QPalette sel_pal;
painter->fillRect(bg_rect, sel_pal.brush(QPalette::Highlight));
fg_pen.setColor(sel_pal.color(QPalette::HighlightedText));
// Highlighted lifelines
painter->save();
QPen hl_pen = fg_pen;
hl_pen.setStyle(Qt::DashLine);
painter->setPen(hl_pen);
painter->setOpacity(alpha);
for (int ll_x = value_axis_->range().lower; ll_x < value_axis_->range().upper; ll_x++) {
// Only draw where we have arrows.
if (ll_x < 0 || ll_x >= value_axis_->tickVector().size()) continue;
QPoint ll_start(coordsToPixels(cur_key - 0.5, ll_x).toPoint());
QPoint ll_end(coordsToPixels(cur_key + 0.5, ll_x).toPoint());
hl_pen.setDashOffset(bg_rect.top() - ll_start.x());
painter->drawLine(ll_start, ll_end);
}
painter->restore();
painter->restore();
bg_color = sel_pal.color(QPalette::Highlight);
} else {
fg_pen.setColor(Qt::black);
bg_color = ColorUtils::sequenceColor(sai->conv_num);
}
// Highlighted background
// painter->save();
QRect bg_rect(
QPoint(coordsToPixels(cur_key - 0.5, value_axis_->range().lower).toPoint()),
QPoint(coordsToPixels(cur_key + 0.5, value_axis_->range().upper).toPoint()));
painter->fillRect(bg_rect, bg_color);
// painter->restore();
// Highlighted lifelines
painter->save();
QPen hl_pen = fg_pen;
hl_pen.setStyle(Qt::DashLine);
painter->setPen(hl_pen);
painter->setOpacity(alpha);
for (int ll_x = value_axis_->range().lower; ll_x < value_axis_->range().upper; ll_x++) {
// Only draw where we have arrows.
if (ll_x < 0 || ll_x >= value_axis_->tickVector().size()) continue;
QPoint ll_start(coordsToPixels(cur_key - 0.5, ll_x).toPoint());
QPoint ll_end(coordsToPixels(cur_key + 0.5, ll_x).toPoint());
hl_pen.setDashOffset(bg_rect.top() - ll_start.x());
painter->drawLine(ll_start, ll_end);
}
painter->restore();
if (cur_key < key_axis_->range().lower || cur_key > key_axis_->range().upper) {
continue;
}
@ -283,14 +289,17 @@ void SequenceDiagram::draw(QCPPainter *painter)
painter->drawText(text_pt, arrow_label);
if (sai->port_src && sai->port_dst) {
QString port_num = QString::number(sai->port_src);
text_pt = QPoint(arrow_start.x() - en_w - (cfm.width(port_num) * dir_mul),
arrow_start.y() + (en_w / 2));
painter->drawText(text_pt, port_num);
int left_x = dir_mul > 0 ? arrow_start.x() : arrow_end.x();
int right_x = dir_mul > 0 ? arrow_end.x() : arrow_start.x();
QString port_left = QString::number(dir_mul > 0 ? sai->port_src : sai->port_dst);
QString port_right = QString::number(dir_mul > 0 ? sai->port_dst : sai->port_src);
port_num = QString::number(sai->port_dst);
text_pt.setX(arrow_end.x() - en_w + (cfm.width(port_num) * dir_mul));
painter->drawText(text_pt, port_num);
text_pt = QPoint(left_x - en_w - cfm.width(port_left),
arrow_start.y() + (en_w / 2));
painter->drawText(text_pt, port_left);
text_pt.setX(right_x + en_w);
painter->drawText(text_pt, port_right);
}
painter->restore();
}

View File

@ -27,6 +27,7 @@
#include "wsutil/nstime.h"
#include "wsutil/utf8_entities.h"
#include "color_utils.h"
#include "progress_frame.h"
#include "sequence_diagram.h"
#include "wireshark_application.h"
@ -37,6 +38,9 @@
#include <QPoint>
// To do:
// - Add zoom controls.
// - Limit dragging to valid ranges.
// - Handle trackpad and mouse wheel scrolling.
// - Add UTF8 to text dump
// - Save to XMI? http://www.umlgraph.org/
// - Time: abs vs delta
@ -46,8 +50,6 @@
// - Change line_style to seq_type (i.e. draw ACKs dashed)
// - Create WSGraph subclasses with common behavior.
// - Help button and text
// - Diagram shrinks when you click on it on retina displays.
// - Add zoom controls.
SequenceDialog::SequenceDialog(QWidget &parent, CaptureFile &cf, SequenceInfo *info) :
WiresharkDialog(parent, cf),
@ -55,7 +57,7 @@ SequenceDialog::SequenceDialog(QWidget &parent, CaptureFile &cf, SequenceInfo *i
info_(info),
num_items_(0),
packet_num_(0),
node_label_w_(20)
sequence_w_(1)
{
ui->setupUi(this);
loadGeometry(parent.width(), parent.height() * 4 / 5);
@ -80,9 +82,32 @@ SequenceDialog::SequenceDialog(QWidget &parent, CaptureFile &cf, SequenceInfo *i
sp->xAxis->setPadding(0);
sp->xAxis->setLabelPadding(0);
sp->xAxis->setTickLabelPadding(0);
QPen base_pen(ColorUtils::alphaBlend(palette().text(), palette().base(), 0.25));
base_pen.setWidthF(0.5);
sp->xAxis2->setBasePen(base_pen);
sp->yAxis->setBasePen(base_pen);
sp->yAxis2->setBasePen(base_pen);
sp->xAxis2->setVisible(true);
sp->yAxis2->setVisible(true);
key_text_ = new QCPItemText(sp);
key_text_->setText(tr("Time"));
sp->addItem(key_text_);
key_text_->setPositionAlignment(Qt::AlignRight | Qt::AlignBottom);
key_text_->position->setType(QCPItemPosition::ptAbsolute);
key_text_->setClipToAxisRect(false);
comment_text_ = new QCPItemText(sp);
comment_text_->setText(tr("Comment"));
sp->addItem(comment_text_);
comment_text_->setPositionAlignment(Qt::AlignLeft | Qt::AlignBottom);
comment_text_->position->setType(QCPItemPosition::ptAbsolute);
comment_text_->setClipToAxisRect(false);
one_em_ = QFontMetrics(sp->yAxis->labelFont()).height();
ui->horizontalScrollBar->setSingleStep(100 / one_em_);
ui->verticalScrollBar->setSingleStep(100 / one_em_);
@ -134,8 +159,6 @@ SequenceDialog::SequenceDialog(QWidget &parent, CaptureFile &cf, SequenceInfo *i
connect(this, SIGNAL(goToPacket(int)), seq_diagram_, SLOT(setSelectedPacket(int)));
disconnect(ui->buttonBox, SIGNAL(accepted()), this, SLOT(accept()));
QTimer::singleShot(0, this, SLOT(fillDiagram()));
}
SequenceDialog::~SequenceDialog()
@ -151,7 +174,7 @@ void SequenceDialog::updateWidgets()
void SequenceDialog::showEvent(QShowEvent *)
{
resetAxes();
QTimer::singleShot(0, this, SLOT(fillDiagram()));
}
void SequenceDialog::resizeEvent(QResizeEvent *)
@ -350,18 +373,8 @@ void SequenceDialog::fillDiagram()
seq_diagram_->setData(info_->sainfo());
}
QFontMetrics vfm = QFontMetrics(sp->xAxis2->labelFont());
char* addr_str;
node_label_w_ = 0;
for (guint i = 0; i < info_->sainfo()->num_nodes; i++) {
addr_str = address_to_display(NULL, &(info_->sainfo()->nodes[i]));
int label_w = vfm.width(addr_str);
if (node_label_w_ < label_w) {
node_label_w_ = label_w;
}
wmem_free(NULL, addr_str);
}
node_label_w_ = (node_label_w_ * 3 / 4) + one_em_;
QFontMetrics fm = QFontMetrics(sp->xAxis2->labelFont());
sequence_w_ = fm.height() * 15 ; // Arbitrary
mouseMoved(NULL);
resetAxes();
@ -394,6 +407,7 @@ void SequenceDialog::resetAxes(bool keep_lower)
if (!info_->sainfo()) return;
QCustomPlot *sp = ui->sequencePlot;
// Allow space for labels on the top and port numbers on the left.
double top_pos = -1.0, left_pos = -0.5;
if (keep_lower) {
@ -401,21 +415,42 @@ void SequenceDialog::resetAxes(bool keep_lower)
left_pos = sp->xAxis2->range().lower;
}
double range_ratio = sp->xAxis2->axisRect()->width() / node_label_w_;
sp->xAxis2->setRange(left_pos, range_ratio + left_pos);
double range_span = sp->viewport().width() / sequence_w_;
sp->xAxis2->setRange(left_pos, range_span + left_pos);
range_ratio = sp->yAxis->axisRect()->height() / (one_em_ * 1.5);
sp->yAxis->setRange(top_pos, range_ratio + top_pos);
range_span = sp->axisRect()->height() / (one_em_ * 1.5);
sp->yAxis->setRange(top_pos, range_span + top_pos);
double rmin = sp->xAxis2->range().size() / 2;
ui->horizontalScrollBar->setRange((rmin - 0.5) * 100, (info_->sainfo()->num_nodes - 0.5 - rmin) * 100);
xAxisChanged(sp->xAxis2->range());
ui->horizontalScrollBar->setValue(ui->horizontalScrollBar->minimum()); // Shouldn't be needed.
rmin = (sp->yAxis->range().size() / 2);
ui->verticalScrollBar->setRange((rmin - 1.0) * 100, (num_items_ - 0.5 - rmin) * 100);
yAxisChanged(sp->yAxis->range());
sp->replot();
// It would be exceedingly handy if we could do one or both of the
// following:
// - Position an axis label above its axis inline with the tick labels.
// - Anchor a QCPItemText to one of the corners of a QCPAxis.
// Neither of those appear to be possible, so we first call replot in
// order to lay out our X axes, place our labels, the call replot again.
sp->replot(QCustomPlot::rpQueued);
QRect axis_rect = sp->axisRect()->rect();
key_text_->position->setCoords(axis_rect.left()
- sp->yAxis->padding()
- sp->yAxis->tickLabelPadding()
- sp->yAxis->offset(),
axis_rect.top());
comment_text_->position->setCoords(axis_rect.right()
+ sp->yAxis2->padding()
+ sp->yAxis2->tickLabelPadding()
+ sp->yAxis2->offset(),
axis_rect.top());
sp->replot(QCustomPlot::rpHint);
}
void SequenceDialog::on_resetButton_clicked()

View File

@ -108,8 +108,10 @@ private:
int num_items_;
guint32 packet_num_;
double one_em_;
int node_label_w_;
int sequence_w_;
QMenu ctx_menu_;
QCPItemText *key_text_;
QCPItemText *comment_text_;
void panAxes(int x_pixels, int y_pixels);
void resetAxes(bool keep_lower = false);