Qt: Add a pref for column text caching, and have it affect sorting

Introduce a preference for the number of rows whose column text can
be cached, and allow sorting of the packet list only when the
number of displayed rows can fit in the cache. This preference only has
an effect for sorting based on columns that require dissection and
caching the column text. This reduces the number of dissections from
O(N log N) to N. Subsequent sorts are even faster.

Columns based on frame data are unaffected, as they sort much faster
as dissection is not required.

Set the size of the QCache introduced in 8c6854fb65 based
on this preference.

Send a temporary status message to the status bar if we try to sort
but there are too many rows, explaining why sorting did not happen and
that the layout preferences can be changed.

Ping #18741
This commit is contained in:
John Thacker 2022-12-26 14:27:02 -05:00
parent 751d836123
commit da3a48f820
8 changed files with 83 additions and 1 deletions

View File

@ -3485,6 +3485,11 @@ prefs_register_modules(void)
"To prevent sorting by mistake (which can take some time to calculate), it can be disabled",
&prefs.gui_packet_list_sortable);
prefs_register_uint_preference(gui_module, "packet_list_cached_rows_max",
"Maximum cached rows",
"Maximum number of rows that can be sorted by columns that require dissection. Increasing this increases memory consumption by caching column text",
10,
&prefs.gui_packet_list_cached_rows_max);
prefs_register_bool_preference(gui_module, "interfaces_show_hidden",
"Show hidden interfaces",
@ -4190,6 +4195,7 @@ pre_init_prefs(void)
prefs.gui_packet_list_show_related = TRUE;
prefs.gui_packet_list_show_minimap = TRUE;
prefs.gui_packet_list_sortable = TRUE;
prefs.gui_packet_list_cached_rows_max = 10000;
g_free (prefs.gui_interfaces_hide_types);
prefs.gui_interfaces_hide_types = g_strdup("");
prefs.gui_interfaces_show_hidden = FALSE;

View File

@ -220,6 +220,7 @@ typedef struct _e_prefs {
gboolean gui_packet_list_show_related;
gboolean gui_packet_list_show_minimap;
gboolean gui_packet_list_sortable;
guint gui_packet_list_cached_rows_max;
gint gui_decimal_places1; /* Used for type 1 calculations */
gint gui_decimal_places2; /* Used for type 2 calculations */
gint gui_decimal_places3; /* Used for type 3 calculations */

View File

@ -41,7 +41,7 @@ LayoutPreferencesFrame::LayoutPreferencesFrame(QWidget *parent) :
QStyleOption style_opt;
QString indent_ss = QString(
"QCheckBox {"
"QCheckBox, QLabel {"
" margin-left: %1px;"
"}"
).arg(ui->packetListSeparatorCheckBox->style()->subElementRect(QStyle::SE_CheckBoxContents, &style_opt).left());
@ -49,6 +49,7 @@ LayoutPreferencesFrame::LayoutPreferencesFrame(QWidget *parent) :
ui->packetListHeaderShowColumnDefinition->setStyleSheet(indent_ss);
ui->packetListHoverStyleCheckbox->setStyleSheet(indent_ss);
ui->packetListAllowSorting->setStyleSheet(indent_ss);
ui->packetListCachedRowsLabel->setStyleSheet(indent_ss);
ui->statusBarShowSelectedPacketCheckBox->setStyleSheet(indent_ss);
ui->statusBarShowFileLoadTimeCheckBox->setStyleSheet(indent_ss);
@ -64,6 +65,8 @@ LayoutPreferencesFrame::LayoutPreferencesFrame(QWidget *parent) :
pref_packet_list_sorting_ = prefFromPrefPtr(&prefs.gui_packet_list_sortable);
ui->packetListAllowSorting->setChecked(prefs_get_bool_value(pref_packet_list_sorting_, pref_stashed));
pref_packet_list_cached_rows_max_ = prefFromPrefPtr(&prefs.gui_packet_list_cached_rows_max);
pref_show_selected_packet_ = prefFromPrefPtr(&prefs.gui_qt_show_selected_packet);
ui->statusBarShowSelectedPacketCheckBox->setChecked(prefs_get_bool_value(pref_show_selected_packet_, pref_stashed));
@ -157,6 +160,8 @@ void LayoutPreferencesFrame::updateWidgets()
ui->pane3NoneRadioButton->setChecked(true);
break;
}
ui->packetListCachedRowsLineEdit->setText(QString::number(prefs_get_uint_value_real(pref_packet_list_cached_rows_max_, pref_stashed)));
}
void LayoutPreferencesFrame::on_layout5ToolButton_toggled(bool checked)
@ -371,6 +376,15 @@ void LayoutPreferencesFrame::on_packetListAllowSorting_toggled(bool checked)
prefs_set_bool_value(pref_packet_list_sorting_, (gboolean) checked, pref_stashed);
}
void LayoutPreferencesFrame::on_packetListCachedRowsLineEdit_textEdited(const QString &new_str)
{
bool ok;
uint new_uint = new_str.toUInt(&ok, 0);
if (ok) {
prefs_set_uint_value(pref_packet_list_cached_rows_max_, new_uint, pref_stashed);
}
}
void LayoutPreferencesFrame::on_statusBarShowSelectedPacketCheckBox_toggled(bool checked)
{
prefs_set_bool_value(pref_show_selected_packet_, (gboolean) checked, pref_stashed);

View File

@ -41,6 +41,7 @@ private:
pref_t *pref_packet_header_column_definition_;
pref_t *pref_packet_list_hover_style_;
pref_t *pref_packet_list_sorting_;
pref_t *pref_packet_list_cached_rows_max_;
pref_t *pref_show_selected_packet_;
pref_t *pref_show_file_load_time_;
@ -73,6 +74,7 @@ private slots:
void on_packetListHeaderShowColumnDefinition_toggled(bool checked);
void on_packetListHoverStyleCheckbox_toggled(bool checked);
void on_packetListAllowSorting_toggled(bool checked);
void on_packetListCachedRowsLineEdit_textEdited(const QString &new_str);
void on_statusBarShowSelectedPacketCheckBox_toggled(bool checked);
void on_statusBarShowFileLoadTimeCheckBox_toggled(bool checked);
};

View File

@ -402,6 +402,34 @@
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="packetListCachedRows">
<item>
<widget class="QLabel" name="packetListCachedRowsLabel">
<property name="text">
<string>Maximum number of cached rows (affects sorting)</string>
</property>
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;If more than this many rows are displayed, then sorting by columns that require packet dissection will be disabled. Increasing this number increases memory consumption by caching column values.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="packetListCachedRowsLineEdit">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;If more than this many rows are displayed, then sorting by columns that require packet dissection will be disabled. Increasing this number increases memory consumption by caching column values.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
</widget>
</item>
<item>
<spacer name="packetListCachedRowsHorizontalSpacer">
<property name="orientation">
<enum>Qt:Horizontal</enum>
</property>
</spacer>
</item>
</layout>
</item>
<item>
<widget class="QCheckBox" name="packetListHoverStyleCheckbox">
<property name="text">

View File

@ -358,6 +358,27 @@ void PacketListModel::sort(int column, Qt::SortOrder order)
QString col_title = get_column_title(column);
if (text_sort_column_ >= 0 && (guint)visible_rows_.count() > prefs.gui_packet_list_cached_rows_max) {
/* Column not based on frame data but by column text that requires
* dissection, so to sort in a reasonable amount of time the column
* text needs to be cached.
*/
/* If the sort is being triggered because the columns were already
* sorted and the filter is being cleared (or changed to something
* else with more rows than fit in the cache), then the temporary
* message will be immediately overwritten with the standard capture
* statistics by the packets_bar_update() call after thawing the rows.
* It will still blink yellow, and the user will get the message if
* they then click on the header file (wondering why it didn't sort.)
*/
if (col_title.isEmpty()) {
col_title = tr("Column");
}
QString temp_msg = tr("%1 can only be sorted with %2 or fewer visible rows; increase cache size in Layout preferences").arg(col_title).arg(prefs.gui_packet_list_cached_rows_max);
mainApp->pushStatus(MainApplication::TemporaryStatus, temp_msg);
return;
}
// XXX Use updateProgress instead. We'd have to switch from std::sort to
// something we can interrupt.
if (!col_title.isEmpty()) {

View File

@ -45,6 +45,10 @@ public:
int columnTextSize(const char *str);
static void invalidateAllRecords() { col_text_cache_.clear(); }
/* In Qt 6, QCache maxCost is a qsizetype, but the QAbstractItemModel
* number of rows is still an int, so we're limited to INT_MAX anyway.
*/
static void setMaxCache(int cost) { col_text_cache_.setMaxCost(cost); }
static void resetColumns(column_info *cinfo);
static void resetColorization() { rows_color_ver_++; }

View File

@ -252,6 +252,12 @@ PacketList::PacketList(QWidget *parent) :
connect(mainApp, SIGNAL(addressResolutionChanged()), this, SLOT(redrawVisiblePacketsDontSelectCurrent()));
connect(mainApp, SIGNAL(columnDataChanged()), this, SLOT(redrawVisiblePacketsDontSelectCurrent()));
connect(mainApp, &MainApplication::preferencesChanged, this, [=]() {
/* The pref is a uint but QCache maxCost is a signed int (/
* qsizetype in Qt 6). Note that QAbstractItemModel row numbers
* are ints, not unsigned ints, so we're limited to INT_MAX
* rows anyway.
*/
PacketListRecord::setMaxCache(prefs.gui_packet_list_cached_rows_max > INT_MAX ? INT_MAX : prefs.gui_packet_list_cached_rows_max);
if ((bool) (prefs.gui_packet_list_sortable) != isSortingEnabled()) {
setSortingEnabled(prefs.gui_packet_list_sortable);
}