Skip to content

Commit 5589bd9

Browse files
committed
Speed up the loading of the database structure
While testing the original database from issue #1892 which contains hundreds of tables and fields, I noticed how long the loading of the database structure takes. This is especially problematic since it needs to be reloaded on various occasions, e.g. running most statements in the Execute SQL tab, which stalls the application every time. According to a profiler it is the QIcon constructor which requires most of the time. By introducing this small icon cache class we can reduce the time for loading icons to almost nothing. While still not perfect the UI already feels much more responsive with this patch.
1 parent 237b1fd commit 5589bd9

File tree

5 files changed

+64
-16
lines changed

5 files changed

+64
-16
lines changed

CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,7 @@ set(SQLB_HDR
127127
src/grammar/Sqlite3Lexer.hpp
128128
src/grammar/Sqlite3Parser.hpp
129129
src/Data.h
130+
src/IconCache.h
130131
)
131132

132133
set(SQLB_MOC_HDR
@@ -228,6 +229,7 @@ set(SQLB_SRC
228229
src/CondFormat.cpp
229230
src/RunSql.cpp
230231
src/ProxyDialog.cpp
232+
src/IconCache.cpp
231233
)
232234

233235
set(SQLB_FORMS

src/DbStructureModel.cpp

Lines changed: 15 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
#include "DbStructureModel.h"
2+
#include <IconCache.h>
23
#include "sqlitedb.h"
34
#include "sqlitetablemodel.h"
45
#include "Settings.h"
@@ -160,12 +161,12 @@ void DbStructureModel::reloadData()
160161
// The second node contains four sub-nodes (tables, indices, views and triggers), each containing a list of objects of that type.
161162
// This way we only have to have and only have to update one model and can use it in all sorts of places, just by setting a different root node.
162163
browsablesRootItem = new QTreeWidgetItem(rootItem);
163-
browsablesRootItem->setIcon(ColumnName, QIcon(QString(":/icons/view")));
164+
browsablesRootItem->setIcon(ColumnName, IconCache::get("view"));
164165
browsablesRootItem->setText(ColumnName, tr("Browsables"));
165166

166167
// Make sure to always load the main schema first
167168
QTreeWidgetItem* itemAll = new QTreeWidgetItem(rootItem);
168-
itemAll->setIcon(ColumnName, QIcon(QString(":/icons/database")));
169+
itemAll->setIcon(ColumnName, IconCache::get("database"));
169170
itemAll->setText(ColumnName, tr("All"));
170171
itemAll->setText(ColumnObjectType, "database");
171172
buildTree(itemAll, "main");
@@ -174,7 +175,7 @@ void DbStructureModel::reloadData()
174175
if(!m_db.schemata["temp"].empty())
175176
{
176177
QTreeWidgetItem* itemTemp = new QTreeWidgetItem(itemAll);
177-
itemTemp->setIcon(ColumnName, QIcon(QString(":/icons/database")));
178+
itemTemp->setIcon(ColumnName, IconCache::get("database"));
178179
itemTemp->setText(ColumnName, tr("Temporary"));
179180
itemTemp->setText(ColumnObjectType, "database");
180181
buildTree(itemTemp, "temp");
@@ -187,7 +188,7 @@ void DbStructureModel::reloadData()
187188
if(it.first != "main" && it.first != "temp")
188189
{
189190
QTreeWidgetItem* itemSchema = new QTreeWidgetItem(itemAll);
190-
itemSchema->setIcon(ColumnName, QIcon(QString(":/icons/database")));
191+
itemSchema->setIcon(ColumnName, IconCache::get("database"));
191192
itemSchema->setText(ColumnName, QString::fromStdString(it.first));
192193
itemSchema->setText(ColumnObjectType, "database");
193194
buildTree(itemSchema, it.first);
@@ -321,22 +322,22 @@ void DbStructureModel::buildTree(QTreeWidgetItem* parent, const std::string& sch
321322

322323
// Prepare tree
323324
QTreeWidgetItem* itemTables = new QTreeWidgetItem(parent);
324-
itemTables->setIcon(ColumnName, QIcon(QString(":/icons/table")));
325+
itemTables->setIcon(ColumnName, IconCache::get("table"));
325326
itemTables->setText(ColumnName, tr("Tables (%1)").arg(calc_number_of_objects_by_type(objmap, "table")));
326327
typeToParentItem.insert({"table", itemTables});
327328

328329
QTreeWidgetItem* itemIndices = new QTreeWidgetItem(parent);
329-
itemIndices->setIcon(ColumnName, QIcon(QString(":/icons/index")));
330+
itemIndices->setIcon(ColumnName, IconCache::get("index"));
330331
itemIndices->setText(ColumnName, tr("Indices (%1)").arg(calc_number_of_objects_by_type(objmap, "index")));
331332
typeToParentItem.insert({"index", itemIndices});
332333

333334
QTreeWidgetItem* itemViews = new QTreeWidgetItem(parent);
334-
itemViews->setIcon(ColumnName, QIcon(QString(":/icons/view")));
335+
itemViews->setIcon(ColumnName, IconCache::get("view"));
335336
itemViews->setText(ColumnName, tr("Views (%1)").arg(calc_number_of_objects_by_type(objmap, "view")));
336337
typeToParentItem.insert({"view", itemViews});
337338

338339
QTreeWidgetItem* itemTriggers = new QTreeWidgetItem(parent);
339-
itemTriggers->setIcon(ColumnName, QIcon(QString(":/icons/trigger")));
340+
itemTriggers->setIcon(ColumnName, IconCache::get("trigger"));
340341
itemTriggers->setText(ColumnName, tr("Triggers (%1)").arg(calc_number_of_objects_by_type(objmap, "trigger")));
341342
typeToParentItem.insert({"trigger", itemTriggers});
342343

@@ -377,24 +378,24 @@ void DbStructureModel::buildTree(QTreeWidgetItem* parent, const std::string& sch
377378
fldItem->setText(ColumnSQL, QString::fromStdString(field.sql));
378379
fldItem->setText(ColumnSchema, QString::fromStdString(schema));
379380
if(contains(pk_columns, field.name))
380-
fldItem->setIcon(ColumnName, QIcon(":/icons/field_key"));
381+
fldItem->setIcon(ColumnName, IconCache::get("field_key"));
381382
else if(isFK)
382-
fldItem->setIcon(ColumnName, QIcon(":/icons/field_fk"));
383+
fldItem->setIcon(ColumnName, IconCache::get("field_fk"));
383384
else
384-
fldItem->setIcon(ColumnName, QIcon(":/icons/field"));
385+
fldItem->setIcon(ColumnName, IconCache::get("field"));
385386
}
386387
}
387388
}
388389
}
389390

390391
QTreeWidgetItem* DbStructureModel::addNode(QTreeWidgetItem* parent, const sqlb::ObjectPtr& object, const std::string& schema)
391392
{
392-
QString type = QString::fromStdString(sqlb::Object::typeToString(object->type()));
393+
std::string type = sqlb::Object::typeToString(object->type());
393394

394395
QTreeWidgetItem *item = new QTreeWidgetItem(parent);
395-
item->setIcon(ColumnName, QIcon(QString(":/icons/%1").arg(type)));
396+
item->setIcon(ColumnName, IconCache::get(type));
396397
item->setText(ColumnName, QString::fromStdString(object->name()));
397-
item->setText(ColumnObjectType, type);
398+
item->setText(ColumnObjectType, QString::fromStdString(type));
398399
item->setText(ColumnSQL, QString::fromStdString(object->originalSql()));
399400
item->setText(ColumnSchema, QString::fromStdString(schema));
400401

src/IconCache.cpp

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
#include "IconCache.h"
2+
3+
QIcon IconCache::null_icon;
4+
std::unordered_map<std::string, QIcon> IconCache::icons;
5+
6+
const QIcon& IconCache::get(const std::string& name)
7+
{
8+
// Check if we already have an icon object with that name in the cache. If so, just return that
9+
auto it = icons.find(name);
10+
if(it != icons.end())
11+
return it->second;
12+
13+
// If now, try to load an icon with that name and insert it into the cache
14+
QIcon icon(":/icons/" + QString::fromStdString(name));
15+
auto ins = icons.insert({name, icon});
16+
17+
// If the insertion was successful, return the inserted icon object. If it was not, return a null icon.
18+
if(ins.second)
19+
return ins.first->second;
20+
else
21+
return null_icon;
22+
}

src/IconCache.h

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
#ifndef ICONCACHE_H
2+
#define ICONCACHE_H
3+
4+
#include <QIcon>
5+
6+
#include <string>
7+
#include <unordered_map>
8+
9+
class IconCache
10+
{
11+
public:
12+
IconCache() = delete;
13+
14+
static const QIcon& get(const std::string& name);
15+
16+
private:
17+
static QIcon null_icon;
18+
static std::unordered_map<std::string, QIcon> icons;
19+
};
20+
21+
#endif

src/src.pro

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,8 @@ HEADERS += \
7575
sql/Query.h \
7676
RunSql.h \
7777
sql/ObjectIdentifier.h \
78-
ProxyDialog.h
78+
ProxyDialog.h \
79+
IconCache.h
7980

8081
SOURCES += \
8182
sqlitedb.cpp \
@@ -127,7 +128,8 @@ SOURCES += \
127128
sql/Query.cpp \
128129
RunSql.cpp \
129130
sql/ObjectIdentifier.cpp \
130-
ProxyDialog.cpp
131+
ProxyDialog.cpp \
132+
IconCache.cpp
131133

132134
RESOURCES += icons/icons.qrc \
133135
translations/flags/flags.qrc \

0 commit comments

Comments
 (0)