Skip to content

Commit e7e11f7

Browse files
committed
#2074 note-relations: implement more multi-threading
Signed-off-by: Patrizio Bekerle <[email protected]>
1 parent 6616e27 commit e7e11f7

File tree

4 files changed

+94
-25
lines changed

4 files changed

+94
-25
lines changed

CHANGELOG.md

+5-1
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,13 @@
11
# QOwnNotes Changelog
22

33
## 25.3.1
4+
- In the **note relation panel** the gathering of note relations will now be done
5+
in a **different thread** (for [#2074](https://github.com/pbek/QOwnNotes/issues/2074))
6+
- This results in a huge speed improvement and being able to interact with the
7+
user interface while the relations are gathered and drawn
8+
- The gathering of note relations and drawing is stopped when another note is selected
49
- Added a **new editor color schema** *Simple* by @nobodyF34R
510
(for [#3238](https://github.com/pbek/QOwnNotes/issues/3238))
6-
- Made a small speed improvement in the note relations panel (for [#2074](https://github.com/pbek/QOwnNotes/issues/2074))
711

812
## 25.3.0
913
- There now is a new **note relations panel**, that shows links to other notes in a graph

src/mainwindow.cpp

+1
Original file line numberDiff line numberDiff line change
@@ -743,6 +743,7 @@ bool MainWindow::restoreActiveNoteHistoryItem() {
743743
}
744744

745745
MainWindow::~MainWindow() {
746+
_noteRelationScene->stopDrawing();
746747
disableFullScreenMode();
747748

748749
const bool forceQuit = qApp->property("clearAppDataAndExit").toBool();

src/widgets/noterelationscene.cpp

+72-22
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,11 @@ void ConnectionLine::updatePosition() {
8989

9090
// NoteRelationScene Implementation
9191
NoteRelationScene::NoteRelationScene(QObject *parent)
92-
: QGraphicsScene(parent), m_connecting(false), m_tempLine(nullptr), m_startItem(nullptr) {}
92+
: QGraphicsScene(parent), m_connecting(false), m_tempLine(nullptr), m_startItem(nullptr) {
93+
// Connect the signal to the slot with a queued connection
94+
connect(this, &NoteRelationScene::addItemRequested, this, &NoteRelationScene::addItemToScene,
95+
Qt::QueuedConnection);
96+
}
9397

9498
void NoteRelationScene::mouseMoveEvent(QGraphicsSceneMouseEvent *event) {
9599
if (m_connecting && m_tempLine && m_startItem) {
@@ -130,48 +134,90 @@ NoteItem *NoteRelationScene::createNoteItem(const QPointF &pos, Note note, int l
130134
auto *noteItem = new NoteItem(note, 0, 0, xpos, ypos, level);
131135
noteItem->setPos(posPoint);
132136
// The scene is taking ownership over the note item
133-
addItem(noteItem);
137+
// addItem(noteItem);
138+
emit addItemRequested(noteItem);
134139

135140
return noteItem;
136141
}
137142

138143
void NoteRelationScene::createConnection(NoteItem *startItem, NoteItem *endItem) {
139144
auto *connection = new ConnectionLine(startItem, endItem);
140-
addItem(connection);
145+
// addItem(connection);
146+
emit addItemRequested(connection);
141147
m_connections.push_back(connection);
142148
}
143149

144-
void NoteRelationScene::drawForNote(const Note& note) {
150+
void NoteRelationScene::addItemToScene(QGraphicsItem *item) {
151+
// This will run in the GUI thread
152+
addItem(item);
153+
}
154+
155+
void NoteRelationScene::drawForNote(const Note &note) {
156+
qDebug() << __func__
157+
<< " - 'this->m_drawFuture.isRunning()': " << this->m_drawFuture.isRunning();
158+
// Disallow more drawing and cancel the current thread
159+
stopDrawing();
160+
161+
// Fetch all notes while waiting for the previous thread to finish
162+
const auto noteList = Note::fetchAll();
163+
164+
// Wait a little more for if the thread is still running
165+
if (this->m_drawFuture.isRunning()) {
166+
this->m_drawFuture.waitForFinished();
167+
}
168+
169+
// Allow drawing again and clear the scene
170+
setAllowDrawing();
145171
m_connections.clear();
146172
clear();
147-
const auto noteList = Note::fetchAll();
148173

149-
#if QT_VERSION >= QT_VERSION_CHECK(5, 4, 0)
150-
QFuture<void> future = QtConcurrent::run([this, note, noteList]() {
174+
// This runs the gathering of note relations in a different thread
175+
// The drawing of the note items and connections will be done in the GUI thread, because the
176+
// QGraphicsView framework is not designed for multithreading (QTimer errors)
177+
// A different database connection to the memory database will also be use, because you cannot
178+
// use the same connection in different threads
179+
this->m_drawFuture = QtConcurrent::run([this, note, noteList]() {
151180
const QString connectionName = DatabaseService::generateConnectionName();
152-
qDebug() << __func__ << " - 'connectionName': " << connectionName;
153181

154-
QSqlDatabase db = DatabaseService::createSharedMemoryDatabase(connectionName);
182+
{
183+
QSqlDatabase db = DatabaseService::createSharedMemoryDatabase(connectionName);
155184

156-
auto rootNoteItem = createNoteItem(QPointF(100, 100), note);
157-
createLinkedNoteItems(noteList, connectionName, note, rootNoteItem);
185+
auto rootNoteItem = createNoteItem(QPointF(100, 100), note);
186+
createLinkedNoteItems(noteList, connectionName, note, rootNoteItem);
158187

159-
// Update all connections
160-
for (auto connection : m_connections) {
161-
connection->updatePosition();
162-
}
188+
// Update all connections
189+
for (auto connection : m_connections) {
190+
connection->updatePosition();
191+
}
163192

164-
// Update the scene
165-
update();
193+
// Update the scene
194+
update();
166195

167-
db.close();
196+
db.close();
197+
} // db goes out of scope and is removed, so the database can be removed
198+
199+
// Remove database connection after the database is closed
168200
QSqlDatabase::removeDatabase(connectionName);
169-
qDebug() << __func__ << " - 'connectionName' closed: " << connectionName;
170201
});
171-
#endif
172202
}
173203

174-
void NoteRelationScene::createLinkedNoteItems(const QVector<Note>& noteList, const QString &connectionName, Note note, NoteItem *rootNoteItem, int level) {
204+
void NoteRelationScene::stopDrawing() {
205+
if (m_drawFuture.isRunning()) {
206+
// Disallow more drawing and cancel the thread
207+
setAllowDrawing(false);
208+
m_drawFuture.cancel();
209+
}
210+
}
211+
212+
void NoteRelationScene::setAllowDrawing(bool allow) { m_allowDrawing = allow; }
213+
214+
void NoteRelationScene::createLinkedNoteItems(const QVector<Note> &noteList,
215+
const QString &connectionName, Note note,
216+
NoteItem *rootNoteItem, int level) {
217+
if (!this->m_allowDrawing) {
218+
return;
219+
}
220+
175221
auto linkedNotes = note.findLinkedNotes(noteList, connectionName);
176222

177223
// Get root note position (center)
@@ -190,9 +236,13 @@ void NoteRelationScene::createLinkedNoteItems(const QVector<Note>& noteList, con
190236
int index = 0;
191237
level = level + 1;
192238
for (auto it = linkedNotes.begin(); it != linkedNotes.end(); ++it) {
239+
if (!this->m_allowDrawing) {
240+
break;
241+
}
242+
193243
// Calculate position in a circle around the root note item
194244
QPointF notePos = calculateRadialPosition(rootCenter, index, linkedNotes.size(), radius);
195-
const Note& linkedNote = it.key();
245+
const Note &linkedNote = it.key();
196246

197247
// Create note item at calculated position
198248
NoteItem *linkedNoteItem = createNoteItem(notePos, linkedNote, level);

src/widgets/noterelationscene.h

+16-2
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313

1414
#pragma once
1515

16+
#include <QFuture>
1617
#include <QGraphicsLineItem>
1718
#include <QGraphicsRectItem>
1819
#include <QGraphicsScene>
@@ -59,7 +60,8 @@ class NoteRelationScene : public QGraphicsScene {
5960

6061
public:
6162
explicit NoteRelationScene(QObject *parent = nullptr);
62-
void drawForNote(const Note& note);
63+
void drawForNote(const Note &note);
64+
void stopDrawing();
6365

6466
protected:
6567
void mouseMoveEvent(QGraphicsSceneMouseEvent *event) override;
@@ -69,9 +71,21 @@ class NoteRelationScene : public QGraphicsScene {
6971
void createConnection(NoteItem *startItem, NoteItem *endItem);
7072

7173
bool m_connecting;
74+
bool m_allowDrawing = true;
7275
QGraphicsLineItem *m_tempLine;
7376
NoteItem *m_startItem;
7477
std::vector<ConnectionLine *> m_connections;
78+
QFuture<void> m_drawFuture;
7579
static QPointF calculateRadialPosition(QPointF center, int index, int total, qreal radius);
76-
void createLinkedNoteItems(const QVector<Note>& noteList, const QString &connectionName, Note note, NoteItem *rootNoteItem, int level = 0);
80+
void createLinkedNoteItems(const QVector<Note> &noteList, const QString &connectionName,
81+
Note note, NoteItem *rootNoteItem, int level = 0);
82+
void setAllowDrawing(bool allow = true);
83+
84+
signals:
85+
// Signal to be emitted from worker thread
86+
void addItemRequested(QGraphicsItem *item);
87+
88+
public slots:
89+
// This will run in the GUI thread
90+
void addItemToScene(QGraphicsItem *item);
7791
};

0 commit comments

Comments
 (0)