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);
this->setSelectionMode(QAbstractItemView::ExtendedSelection);
this->setUniformRowHeights(true);
}
CutterTreeView::~CutterTreeView() {}

View File

@ -8,6 +8,7 @@
#include <QMenu>
#include <QShortcut>
#include <QTreeWidget>
#include <QStandardItemModel>
FlagsModel::FlagsModel(QList<FlagDescription> *flags, QObject *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
{
const FlagDescription &flag = flags->at(index.row());
return flag.offset;
const FlagDescription &flag = flags->at(index.row());
return flag.offset;
}
QString FlagsModel::name(const QModelIndex &index) const
@ -82,6 +83,14 @@ QString FlagsModel::name(const QModelIndex &index) const
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)
: 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
{
FlagDescription left_flag = left.data(FlagsModel::FlagDescriptionRole).value<FlagDescription>();
FlagDescription right_flag = right.data(FlagsModel::FlagDescriptionRole).value<FlagDescription>();
auto source = static_cast<FlagsModel *>(sourceModel());
auto left_flag = source->description(left);
auto right_flag = source->description(right);
switch (left.column()) {
case FlagsModel::SIZE:
if (left_flag.size != right_flag.size)
return left_flag.size < right_flag.size;
if (left_flag->size != right_flag->size)
return left_flag->size < right_flag->size;
// fallthrough
case FlagsModel::OFFSET:
if (left_flag.offset != right_flag.offset)
return left_flag.offset < right_flag.offset;
if (left_flag->offset != right_flag->offset)
return left_flag->offset < right_flag->offset;
// fallthrough
case FlagsModel::NAME:
return left_flag.name < right_flag.name;
return left_flag->name < right_flag->name;
default:
break;
}
// 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)
cur_idx = 0;
disableFlagRefresh = true; // prevent duplicate flag refresh caused by flagspaceCombo modifications
ui->flagspaceCombo->clear();
ui->flagspaceCombo->addItem(tr("(all)"));
@ -221,12 +232,16 @@ void FlagsWidget::refreshFlagspaces()
if (cur_idx > 0)
ui->flagspaceCombo->setCurrentIndex(cur_idx);
disableFlagRefresh = false;
refreshFlags();
}
void FlagsWidget::refreshFlags()
{
if (disableFlagRefresh) {
return;
}
QString flagspace;
QVariant flagspace_data = ui->flagspaceCombo->currentData();
@ -238,10 +253,8 @@ void FlagsWidget::refreshFlags()
flags = Core()->getAllFlags(flagspace);
flags_model->endResetModel();
qhelpers::adjustColumns(ui->flagsTreeView, 2, 0);
tree->showItemsNumber(flags_proxy_model->rowCount());
// TODO: this is not a very good place for the following:
QStringList flagNames;
for (const FlagDescription &i : flags)

View File

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

View File

@ -88,6 +88,11 @@ RVA StringsModel::address(const QModelIndex &index) const
return str.vaddr;
}
const StringDescription *StringsModel::description(const QModelIndex &index) const
{
return &strings->at(index.row());
}
StringsProxyModel::StringsProxyModel(StringsModel *sourceModel, QObject *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
{
auto leftStr = left.data(StringsModel::StringDescriptionRole).value<StringDescription>();
auto rightStr = right.data(StringsModel::StringDescriptionRole).value<StringDescription>();
auto model = static_cast<StringsModel *>(sourceModel());
auto leftStr = model->description(left);
auto rightStr = model->description(right);
switch (left.column()) {
case StringsModel::OffsetColumn:
return leftStr.vaddr < rightStr.vaddr;
return leftStr->vaddr < rightStr->vaddr;
case StringsModel::StringColumn: // sort by string
return leftStr.string < rightStr.string;
return leftStr->string < rightStr->string;
case StringsModel::TypeColumn: // sort by type
return leftStr.type < rightStr.type;
return leftStr->type < rightStr->type;
case StringsModel::SizeColumn: // sort by size
return leftStr.size < rightStr.size;
return leftStr->size < rightStr->size;
case StringsModel::LengthColumn: // sort by length
return leftStr.length < rightStr.length;
return leftStr->length < rightStr->length;
case StringsModel::SectionColumn:
return leftStr.section < rightStr.section;
return leftStr->section < rightStr->section;
default:
break;
}
// fallback
return leftStr.vaddr < rightStr.vaddr;
return leftStr->vaddr < rightStr->vaddr;
}
StringsWidget::StringsWidget(MainWindow *main, QAction *action) :
CutterDockWidget(main, action),
ui(new Ui::StringsWidget),
@ -162,7 +167,7 @@ StringsWidget::StringsWidget(MainWindow *main, QAction *action) :
proxyModel = new StringsProxyModel(model, this);
ui->stringsTreeView->setMainWindow(main);
ui->stringsTreeView->setModel(proxyModel);
ui->stringsTreeView->sortByColumn(StringsModel::OffsetColumn, Qt::AscendingOrder);
ui->stringsTreeView->sortByColumn(-1, Qt::AscendingOrder);
//
auto menu = ui->stringsTreeView->getItemContextMenu();
@ -176,7 +181,8 @@ StringsWidget::StringsWidget(MainWindow *main, QAction *action) :
});
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);
QShortcut *clearShortcut = new QShortcut(QKeySequence(Qt::Key_Escape), this);
@ -197,6 +203,12 @@ StringsWidget::StringsWidget(MainWindow *main, QAction *action) :
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() {}
@ -235,10 +247,6 @@ void StringsWidget::stringSearchFinished(const QList<StringDescription> &strings
this->strings = strings;
model->endResetModel();
qhelpers::adjustColumns(ui->stringsTreeView, 5, 0);
if (ui->stringsTreeView->columnWidth(1) > 300)
ui->stringsTreeView->setColumnWidth(1, 300);
tree->showItemsNumber(proxyModel->rowCount());
task.clear();

View File

@ -39,9 +39,11 @@ public:
int columnCount(const QModelIndex &parent = QModelIndex()) 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;
const StringDescription *description(const QModelIndex &index) const;
};

View File

@ -160,9 +160,11 @@ VTablesWidget::VTablesWidget(MainWindow *main, QAction *action) :
connect(ui->quickFilterView, &QuickFilterView::filterTextChanged, this, [this] {
tree->showItemsNumber(proxy->rowCount());
});
connect(Core(), &CutterCore::codeRebased, this, &VTablesWidget::refreshVTables);
connect(Core(), &CutterCore::refreshAll, this, &VTablesWidget::refreshVTables);
refreshDeferrer = createRefreshDeferrer([this]() { refreshVTables(); });
}
VTablesWidget::~VTablesWidget()
@ -171,6 +173,10 @@ VTablesWidget::~VTablesWidget()
void VTablesWidget::refreshVTables()
{
if (!refreshDeferrer->attemptRefresh(nullptr)) {
return;
}
model->beginResetModel();
vtables = Core()->getAllVTables();
model->endResetModel();

View File

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