Improve flag and string widget performance (#2021)

* Deffer vtable refresh.
* Use uniformRowHeight mode
* Adjust StringsWidget column resizing mode.
This commit is contained in:
karliss 2020-01-20 20:36:32 +02:00 committed by GitHub
parent 8234713a42
commit e0c80182dd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 68 additions and 32 deletions

View File

@ -7,6 +7,7 @@ CutterTreeView::CutterTreeView(QWidget *parent) :
{ {
ui->setupUi(this); ui->setupUi(this);
this->setSelectionMode(QAbstractItemView::ExtendedSelection); this->setSelectionMode(QAbstractItemView::ExtendedSelection);
this->setUniformRowHeights(true);
} }
CutterTreeView::~CutterTreeView() {} CutterTreeView::~CutterTreeView() {}

View File

@ -8,6 +8,7 @@
#include <QMenu> #include <QMenu>
#include <QShortcut> #include <QShortcut>
#include <QTreeWidget> #include <QTreeWidget>
#include <QStandardItemModel>
FlagsModel::FlagsModel(QList<FlagDescription> *flags, QObject *parent) FlagsModel::FlagsModel(QList<FlagDescription> *flags, QObject *parent)
: AddressableItemModel<QAbstractListModel>(parent), : AddressableItemModel<QAbstractListModel>(parent),
@ -72,8 +73,8 @@ QVariant FlagsModel::headerData(int section, Qt::Orientation, int role) const
RVA FlagsModel::address(const QModelIndex &index) const RVA FlagsModel::address(const QModelIndex &index) const
{ {
const FlagDescription &flag = flags->at(index.row()); const FlagDescription &flag = flags->at(index.row());
return flag.offset; return flag.offset;
} }
QString FlagsModel::name(const QModelIndex &index) const QString FlagsModel::name(const QModelIndex &index) const
@ -82,6 +83,14 @@ QString FlagsModel::name(const QModelIndex &index) const
return flag.name; return flag.name;
} }
const FlagDescription *FlagsModel::description(QModelIndex index) const
{
if (index.row() < flags->size()) {
return &flags->at(index.row());
}
return nullptr;
}
FlagsSortFilterProxyModel::FlagsSortFilterProxyModel(FlagsModel *source_model, QObject *parent) FlagsSortFilterProxyModel::FlagsSortFilterProxyModel(FlagsModel *source_model, QObject *parent)
: AddressableFilterProxyModel(source_model, parent) : AddressableFilterProxyModel(source_model, parent)
{ {
@ -96,26 +105,27 @@ bool FlagsSortFilterProxyModel::filterAcceptsRow(int row, const QModelIndex &par
bool FlagsSortFilterProxyModel::lessThan(const QModelIndex &left, const QModelIndex &right) const bool FlagsSortFilterProxyModel::lessThan(const QModelIndex &left, const QModelIndex &right) const
{ {
FlagDescription left_flag = left.data(FlagsModel::FlagDescriptionRole).value<FlagDescription>(); auto source = static_cast<FlagsModel *>(sourceModel());
FlagDescription right_flag = right.data(FlagsModel::FlagDescriptionRole).value<FlagDescription>(); auto left_flag = source->description(left);
auto right_flag = source->description(right);
switch (left.column()) { switch (left.column()) {
case FlagsModel::SIZE: case FlagsModel::SIZE:
if (left_flag.size != right_flag.size) if (left_flag->size != right_flag->size)
return left_flag.size < right_flag.size; return left_flag->size < right_flag->size;
// fallthrough // fallthrough
case FlagsModel::OFFSET: case FlagsModel::OFFSET:
if (left_flag.offset != right_flag.offset) if (left_flag->offset != right_flag->offset)
return left_flag.offset < right_flag.offset; return left_flag->offset < right_flag->offset;
// fallthrough // fallthrough
case FlagsModel::NAME: case FlagsModel::NAME:
return left_flag.name < right_flag.name; return left_flag->name < right_flag->name;
default: default:
break; break;
} }
// fallback // fallback
return left_flag.offset < right_flag.offset; return left_flag->offset < right_flag->offset;
} }
@ -212,6 +222,7 @@ void FlagsWidget::refreshFlagspaces()
if (cur_idx < 0) if (cur_idx < 0)
cur_idx = 0; cur_idx = 0;
disableFlagRefresh = true; // prevent duplicate flag refresh caused by flagspaceCombo modifications
ui->flagspaceCombo->clear(); ui->flagspaceCombo->clear();
ui->flagspaceCombo->addItem(tr("(all)")); ui->flagspaceCombo->addItem(tr("(all)"));
@ -221,12 +232,16 @@ void FlagsWidget::refreshFlagspaces()
if (cur_idx > 0) if (cur_idx > 0)
ui->flagspaceCombo->setCurrentIndex(cur_idx); ui->flagspaceCombo->setCurrentIndex(cur_idx);
disableFlagRefresh = false;
refreshFlags(); refreshFlags();
} }
void FlagsWidget::refreshFlags() void FlagsWidget::refreshFlags()
{ {
if (disableFlagRefresh) {
return;
}
QString flagspace; QString flagspace;
QVariant flagspace_data = ui->flagspaceCombo->currentData(); QVariant flagspace_data = ui->flagspaceCombo->currentData();
@ -238,8 +253,6 @@ void FlagsWidget::refreshFlags()
flags = Core()->getAllFlags(flagspace); flags = Core()->getAllFlags(flagspace);
flags_model->endResetModel(); flags_model->endResetModel();
qhelpers::adjustColumns(ui->flagsTreeView, 2, 0);
tree->showItemsNumber(flags_proxy_model->rowCount()); tree->showItemsNumber(flags_proxy_model->rowCount());
// TODO: this is not a very good place for the following: // TODO: this is not a very good place for the following:

View File

@ -5,6 +5,7 @@
#include <QAbstractItemModel> #include <QAbstractItemModel>
#include <QSortFilterProxyModel> #include <QSortFilterProxyModel>
#include <QStandardItemModel>
#include "core/Cutter.h" #include "core/Cutter.h"
#include "CutterDockWidget.h" #include "CutterDockWidget.h"
@ -34,10 +35,13 @@ public:
int columnCount(const QModelIndex &parent = QModelIndex()) const override; int columnCount(const QModelIndex &parent = QModelIndex()) const override;
QVariant data(const QModelIndex &index, int role) const override; QVariant data(const QModelIndex &index, int role) const override;
QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; QVariant headerData(int section, Qt::Orientation orientation,
int role = Qt::DisplayRole) const override;
RVA address(const QModelIndex &index) const override; RVA address(const QModelIndex &index) const override;
QString name(const QModelIndex &index) const override; QString name(const QModelIndex &index) const override;
const FlagDescription *description(QModelIndex index) const;
}; };
@ -81,6 +85,7 @@ private:
std::unique_ptr<Ui::FlagsWidget> ui; std::unique_ptr<Ui::FlagsWidget> ui;
MainWindow *main; MainWindow *main;
bool disableFlagRefresh = false;
FlagsModel *flags_model; FlagsModel *flags_model;
FlagsSortFilterProxyModel *flags_proxy_model; FlagsSortFilterProxyModel *flags_proxy_model;
QList<FlagDescription> flags; QList<FlagDescription> flags;

View File

@ -88,6 +88,11 @@ RVA StringsModel::address(const QModelIndex &index) const
return str.vaddr; return str.vaddr;
} }
const StringDescription *StringsModel::description(const QModelIndex &index) const
{
return &strings->at(index.row());
}
StringsProxyModel::StringsProxyModel(StringsModel *sourceModel, QObject *parent) StringsProxyModel::StringsProxyModel(StringsModel *sourceModel, QObject *parent)
: AddressableFilterProxyModel(sourceModel, parent) : AddressableFilterProxyModel(sourceModel, parent)
{ {
@ -107,31 +112,31 @@ bool StringsProxyModel::filterAcceptsRow(int row, const QModelIndex &parent) con
bool StringsProxyModel::lessThan(const QModelIndex &left, const QModelIndex &right) const bool StringsProxyModel::lessThan(const QModelIndex &left, const QModelIndex &right) const
{ {
auto leftStr = left.data(StringsModel::StringDescriptionRole).value<StringDescription>(); auto model = static_cast<StringsModel *>(sourceModel());
auto rightStr = right.data(StringsModel::StringDescriptionRole).value<StringDescription>(); auto leftStr = model->description(left);
auto rightStr = model->description(right);
switch (left.column()) { switch (left.column()) {
case StringsModel::OffsetColumn: case StringsModel::OffsetColumn:
return leftStr.vaddr < rightStr.vaddr; return leftStr->vaddr < rightStr->vaddr;
case StringsModel::StringColumn: // sort by string case StringsModel::StringColumn: // sort by string
return leftStr.string < rightStr.string; return leftStr->string < rightStr->string;
case StringsModel::TypeColumn: // sort by type case StringsModel::TypeColumn: // sort by type
return leftStr.type < rightStr.type; return leftStr->type < rightStr->type;
case StringsModel::SizeColumn: // sort by size case StringsModel::SizeColumn: // sort by size
return leftStr.size < rightStr.size; return leftStr->size < rightStr->size;
case StringsModel::LengthColumn: // sort by length case StringsModel::LengthColumn: // sort by length
return leftStr.length < rightStr.length; return leftStr->length < rightStr->length;
case StringsModel::SectionColumn: case StringsModel::SectionColumn:
return leftStr.section < rightStr.section; return leftStr->section < rightStr->section;
default: default:
break; break;
} }
// fallback // fallback
return leftStr.vaddr < rightStr.vaddr; return leftStr->vaddr < rightStr->vaddr;
} }
StringsWidget::StringsWidget(MainWindow *main, QAction *action) : StringsWidget::StringsWidget(MainWindow *main, QAction *action) :
CutterDockWidget(main, action), CutterDockWidget(main, action),
ui(new Ui::StringsWidget), ui(new Ui::StringsWidget),
@ -162,7 +167,7 @@ StringsWidget::StringsWidget(MainWindow *main, QAction *action) :
proxyModel = new StringsProxyModel(model, this); proxyModel = new StringsProxyModel(model, this);
ui->stringsTreeView->setMainWindow(main); ui->stringsTreeView->setMainWindow(main);
ui->stringsTreeView->setModel(proxyModel); ui->stringsTreeView->setModel(proxyModel);
ui->stringsTreeView->sortByColumn(StringsModel::OffsetColumn, Qt::AscendingOrder); ui->stringsTreeView->sortByColumn(-1, Qt::AscendingOrder);
// //
auto menu = ui->stringsTreeView->getItemContextMenu(); auto menu = ui->stringsTreeView->getItemContextMenu();
@ -176,7 +181,8 @@ StringsWidget::StringsWidget(MainWindow *main, QAction *action) :
}); });
QShortcut *searchShortcut = new QShortcut(QKeySequence::Find, this); QShortcut *searchShortcut = new QShortcut(QKeySequence::Find, this);
connect(searchShortcut, &QShortcut::activated, ui->quickFilterView, &ComboQuickFilterView::showFilter); connect(searchShortcut, &QShortcut::activated, ui->quickFilterView,
&ComboQuickFilterView::showFilter);
searchShortcut->setContext(Qt::WidgetWithChildrenShortcut); searchShortcut->setContext(Qt::WidgetWithChildrenShortcut);
QShortcut *clearShortcut = new QShortcut(QKeySequence(Qt::Key_Escape), this); QShortcut *clearShortcut = new QShortcut(QKeySequence(Qt::Key_Escape), this);
@ -197,6 +203,12 @@ StringsWidget::StringsWidget(MainWindow *main, QAction *action) :
tree->showItemsNumber(proxyModel->rowCount()); tree->showItemsNumber(proxyModel->rowCount());
} }
); );
auto header = ui->stringsTreeView->header();
header->setSectionResizeMode(QHeaderView::ResizeMode::ResizeToContents);
header->setSectionResizeMode(StringsModel::StringColumn, QHeaderView::ResizeMode::Stretch);
header->setStretchLastSection(false);
header->setResizeContentsPrecision(256);
} }
StringsWidget::~StringsWidget() {} StringsWidget::~StringsWidget() {}
@ -235,10 +247,6 @@ void StringsWidget::stringSearchFinished(const QList<StringDescription> &strings
this->strings = strings; this->strings = strings;
model->endResetModel(); model->endResetModel();
qhelpers::adjustColumns(ui->stringsTreeView, 5, 0);
if (ui->stringsTreeView->columnWidth(1) > 300)
ui->stringsTreeView->setColumnWidth(1, 300);
tree->showItemsNumber(proxyModel->rowCount()); tree->showItemsNumber(proxyModel->rowCount());
task.clear(); task.clear();

View File

@ -39,9 +39,11 @@ public:
int columnCount(const QModelIndex &parent = QModelIndex()) const override; int columnCount(const QModelIndex &parent = QModelIndex()) const override;
QVariant data(const QModelIndex &index, int role) const override; QVariant data(const QModelIndex &index, int role) const override;
QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; QVariant headerData(int section, Qt::Orientation orientation,
int role = Qt::DisplayRole) const override;
RVA address(const QModelIndex &index) const override; RVA address(const QModelIndex &index) const override;
const StringDescription *description(const QModelIndex &index) const;
}; };

View File

@ -163,6 +163,8 @@ VTablesWidget::VTablesWidget(MainWindow *main, QAction *action) :
connect(Core(), &CutterCore::codeRebased, this, &VTablesWidget::refreshVTables); connect(Core(), &CutterCore::codeRebased, this, &VTablesWidget::refreshVTables);
connect(Core(), &CutterCore::refreshAll, this, &VTablesWidget::refreshVTables); connect(Core(), &CutterCore::refreshAll, this, &VTablesWidget::refreshVTables);
refreshDeferrer = createRefreshDeferrer([this]() { refreshVTables(); });
} }
VTablesWidget::~VTablesWidget() VTablesWidget::~VTablesWidget()
@ -171,6 +173,10 @@ VTablesWidget::~VTablesWidget()
void VTablesWidget::refreshVTables() void VTablesWidget::refreshVTables()
{ {
if (!refreshDeferrer->attemptRefresh(nullptr)) {
return;
}
model->beginResetModel(); model->beginResetModel();
vtables = Core()->getAllVTables(); vtables = Core()->getAllVTables();
model->endResetModel(); model->endResetModel();

View File

@ -70,6 +70,7 @@ private:
QSortFilterProxyModel *proxy; QSortFilterProxyModel *proxy;
QList<VTableDescription> vtables; QList<VTableDescription> vtables;
CutterTreeWidget *tree; CutterTreeWidget *tree;
RefreshDeferrer *refreshDeferrer;
}; };
#endif // VTABLESWIDGET_H #endif // VTABLESWIDGET_H