diff --git a/sources/dataBase/projectdatabase.cpp b/sources/dataBase/projectdatabase.cpp index 1549f6ce0..df686fe12 100644 --- a/sources/dataBase/projectdatabase.cpp +++ b/sources/dataBase/projectdatabase.cpp @@ -53,6 +53,7 @@ projectDataBase::projectDataBase(QETProject *project, QObject *parent) : }); connect(m_project, &QETProject::projectDiagramsOrderChanged, [this]() { + m_content_changed = true; for (auto diagram : m_project->diagrams()) { m_diagram_order_changed.bindValue(":pos", m_project->folioIndex(diagram)+1); @@ -85,14 +86,42 @@ projectDataBase::~projectDataBase() */ void projectDataBase::updateDB() { + //A bulk operation is in progress and updates the database itself once + //it is done : rebuilding now would only be thrown away by that final + //rebuild. @see setUpdateBlocked(). + if (m_update_blocked) { + return; + } + + //Nothing in the project has changed since the last rebuild, so + //repopulating would insert exactly the rows that are already there. + //The signal is still emitted : callers and models rely on it to + //refresh, and what they read back is unchanged either way. + if (!m_content_changed) + { + emit dataBaseUpdated(); + return; + } + populateDiagramTable(); populateDiagramInfoTable(); populateElementTable(); populateElementInfoTable(); populateConductorTable(); + m_content_changed = false; + emit dataBaseUpdated(); } +/** + @brief projectDataBase::setUpdateBlocked + @param blocked : whether updateDB() should skip the full rebuild +*/ +void projectDataBase::setUpdateBlocked(bool blocked) +{ + m_update_blocked = blocked; +} + /** @brief projectDataBase::project @return the project of this database @@ -158,6 +187,7 @@ int projectDataBase::excludedConductorCount() const */ void projectDataBase::addElement(Element *element) { + m_content_changed = true; if (!element || !element->diagram()) { qDebug() << "projectDataBase::addElement: null element or diagram"; return; @@ -182,6 +212,7 @@ void projectDataBase::addElement(Element *element) */ void projectDataBase::removeElement(Element *element) { + m_content_changed = true; m_remove_element_query.bindValue(":uuid", element->uuid().toString()); if(!m_remove_element_query.exec()) { qDebug() << "projectDataBase::removeElement remove error : " << m_remove_element_query.lastError(); @@ -196,6 +227,7 @@ void projectDataBase::removeElement(Element *element) */ void projectDataBase::elementInfoChanged(Element *element) { + m_content_changed = true; auto hash = elementInfoToString(element); for (auto str : QETInformation::elementInfoKeys()) { m_update_element_query.bindValue(":" + str, hash.value(str)); @@ -210,6 +242,7 @@ void projectDataBase::elementInfoChanged(Element *element) void projectDataBase::elementInfoChanged(QList elements) { + m_content_changed = true; this->blockSignals(true); //Block signal for not emit dataBaseUpdated at //each call of the method elementInfoChanged(Element *element) @@ -226,6 +259,7 @@ void projectDataBase::elementInfoChanged(QList elements) void projectDataBase::addDiagram(Diagram *diagram) { + m_content_changed = true; m_insert_diagram_query.bindValue(":uuid", diagram->uuid().toString()); m_insert_diagram_query.bindValue(":pos", m_project->folioIndex(diagram)+1); if(!m_insert_diagram_query.exec()) { @@ -254,6 +288,7 @@ void projectDataBase::addDiagram(Diagram *diagram) void projectDataBase::removeDiagram(Diagram *diagram) { + m_content_changed = true; const QString uuid_str = diagram->uuid().toString(); //Order matters: element_info and terminal are scoped through a @@ -309,6 +344,7 @@ void projectDataBase::removeDiagram(Diagram *diagram) void projectDataBase::diagramInfoChanged(Diagram *diagram) { + m_content_changed = true; bindDiagramInfoValues(m_update_diagram_info_query, diagram); if (!m_update_diagram_info_query.exec()) { @@ -320,6 +356,7 @@ void projectDataBase::diagramInfoChanged(Diagram *diagram) void projectDataBase::diagramOrderChanged() { + m_content_changed = true; } /** @@ -328,6 +365,7 @@ void projectDataBase::diagramOrderChanged() */ void projectDataBase::addConductor(Conductor *conductor) { + m_content_changed = true; if (!conductor || !conductor->diagram()) { qDebug() << "projectDataBase::addConductor: null conductor or diagram"; return; @@ -361,6 +399,7 @@ void projectDataBase::addConductor(Conductor *conductor) */ void projectDataBase::removeConductor(Conductor *conductor) { + m_content_changed = true; m_remove_conductor_query.bindValue(":uuid", conductor->uuid().toString()); if (!m_remove_conductor_query.exec()) { qDebug() << "projectDataBase::removeConductor delete error : " << m_remove_conductor_query.lastError(); @@ -381,6 +420,7 @@ void projectDataBase::removeConductor(Conductor *conductor) */ void projectDataBase::updateConductor(Conductor *conductor) { + m_content_changed = true; if (!conductor) { return; } @@ -423,6 +463,7 @@ void projectDataBase::watchConductor(Conductor *conductor) */ void projectDataBase::conductorPropertiesChanged() { + m_content_changed = true; if (auto *conductor = qobject_cast(sender())) { updateConductor(conductor); } diff --git a/sources/dataBase/projectdatabase.h b/sources/dataBase/projectdatabase.h index 7964bb093..2ce9266bb 100644 --- a/sources/dataBase/projectdatabase.h +++ b/sources/dataBase/projectdatabase.h @@ -47,6 +47,17 @@ class projectDataBase : public QObject virtual ~projectDataBase() override; void updateDB(); + /** + Suppress the full rebuild performed by updateDB(). + + While blocked, updateDB() returns immediately instead of + repopulating every table. Meant for bulk operations -- notably + loading a project, where each table model re-queries the + database as it is built and would otherwise trigger one + complete rebuild of it. The caller unblocks and calls + updateDB() once when done; @see QETProject::readProjectXml(). + */ + void setUpdateBlocked(bool blocked); QETProject *project() const; QSqlQuery newQuery(const QString &query = QString()); QSqlDatabase database() const {return m_data_base;} @@ -97,6 +108,12 @@ class projectDataBase : public QObject private: QPointer m_project; + bool m_update_blocked = false; + //Starts true : the database is empty until the first rebuild. + //Set by every method of this class that writes rows, cleared by + //updateDB(). Callers reach the database from outside only through + //newQuery(), and every such call site reads. + bool m_content_changed = true; QSqlDatabase m_data_base; QSqlQuery m_insert_elements_query, m_insert_element_info_query, diff --git a/sources/qetproject.cpp b/sources/qetproject.cpp index be8df383f..18429779d 100644 --- a/sources/qetproject.cpp +++ b/sources/qetproject.cpp @@ -1547,6 +1547,11 @@ void QETProject::readProjectXml(QDomDocument &xml_project) } m_data_base.blockSignals(true); + //Blocking the signals is not enough : every table model built below + //re-queries the database, and each of those queries used to trigger a + //complete rebuild of it. The content being loaded is the same for all + //of them, so a single rebuild once everything is in place is enough. + m_data_base.setUpdateBlocked(true); //Load the project-wide properties readProjectPropertiesXml(xml_project); @@ -1586,6 +1591,7 @@ void QETProject::readProjectXml(QDomDocument &xml_project) const qint64 refresh_ms = phase_timer.restart(); m_data_base.blockSignals(false); + m_data_base.setUpdateBlocked(false); m_data_base.updateDB(); const qint64 database_ms = phase_timer.elapsed();