Re: Speeding up annotation

Robert Krawitz <rlk-FrUbXkNCsVf2fBVCVOL8/[email protected]> Sat, 11 Jan 2020 23:24:58 -0500 (EST)
Newsgroups gmane.comp.kde.kimdaba
Message-ID <20200112042458.5AB1A4249B2@localhost>
Here are the two patches for annotation dialog:

- patch-annotation-selection.patch improves the selection
  regeneration.

- patch-annotation-2.patch improves the recomputation of the options,
  by improving the computation of the selected/unselected sets, and by
  only applying changes rather than reapplying everything.

Combined, it goes from taking a few minutes to apply a change to
30,000 images to taking a second or so.

-- 
Robert Krawitz                                     <rlk-FrUbXkNCsVf2fBVCVOL8/[email protected]>

***  MIT Engineers   A Proud Tradition   http://mitathletics.com  ***
Member of the League for Programming Freedom  --  http://ProgFree.org
Project lead for Gutenprint   --    http://gimp-print.sourceforge.net

"Linux doesn't dictate how I work, I dictate how Linux works."
--Eric Crampton

_______________________________________________
KPhotoAlbum mailing list
[email protected]
https://mail.kdab.com/mailman/listinfo/kphotoalbum
patch-annotation-2.patch (application/octet-stream, 6.5 KB)
diff --git a/AnnotationDialog/Dialog.cpp b/AnnotationDialog/Dialog.cpp
index a3030df7..1c958588 100644
--- a/AnnotationDialog/Dialog.cpp
+++ b/AnnotationDialog/Dialog.cpp
@@ -1043,6 +1043,17 @@ void AnnotationDialog::Dialog::closeDialog()
     QDialog::reject();
 }
 
+StringSet AnnotationDialog::Dialog::changedOptions(ListSelect *ls)
+{
+    StringSet on, partialOn, off, changes;
+    std::tie(on, partialOn, off) = selectionForMultiSelect(ls, m_origList);
+    changes += (ls->itemsOn() - on);
+    changes += (on - ls->itemsOn());
+    changes += (ls->itemsOff() - off);
+    changes += (off - ls->itemsOff());
+    return changes;
+}
+
 bool AnnotationDialog::Dialog::hasChanges()
 {
     bool changed = false;
@@ -1057,14 +1068,9 @@ bool AnnotationDialog::Dialog::hasChanges()
     else if (m_setup == InputMultiImageConfigMode) {
         changed |= (!m_startDate->date().isNull());
         changed |= (!m_endDate->date().isNull());
-
         Q_FOREACH (ListSelect *ls, m_optionList) {
-            StringSet on, partialOn;
-            std::tie(on, partialOn) = selectionForMultiSelect(ls, m_origList);
-            changed |= (on != ls->itemsOn());
-            changed |= (partialOn != ls->itemsUnchanged());
+            changed |= !(changedOptions(ls).isEmpty());
         }
-
         changed |= (!m_imageLabel->text().isEmpty());
         changed |= (m_description->toPlainText() != m_firstDescription);
         changed |= m_ratingChanged;
@@ -1323,28 +1329,41 @@ KActionCollection *AnnotationDialog::Dialog::actions()
 
 void AnnotationDialog::Dialog::setUpCategoryListBoxForMultiImageSelection(ListSelect *listSel, const DB::ImageInfoList &images)
 {
-    StringSet on, partialOn;
-    std::tie(on, partialOn) = selectionForMultiSelect(listSel, images);
+    StringSet on, partialOn, off;
+    std::tie(on, partialOn, off) = selectionForMultiSelect(listSel, images);
     listSel->setSelection(on, partialOn);
 }
 
-std::tuple<StringSet, StringSet> AnnotationDialog::Dialog::selectionForMultiSelect(ListSelect *listSel, const DB::ImageInfoList &images)
+std::tuple<StringSet, StringSet, StringSet> AnnotationDialog::Dialog::selectionForMultiSelect(ListSelect *listSel, const DB::ImageInfoList &images)
 {
     const QString category = listSel->category();
     const StringSet allItems = DB::ImageDB::instance()->categoryCollection()->categoryForName(category)->itemsInclCategories().toSet();
-    StringSet itemsNotSelectedOnAllImages;
     StringSet itemsOnSomeImages;
+    StringSet itemsOnAllImages;
+    bool firstImage = true;
 
     for (DB::ImageInfoList::ConstIterator imageIt = images.begin(); imageIt != images.end(); ++imageIt) {
         const StringSet itemsOnThisImage = (*imageIt)->itemsOfCategory(category);
-        itemsNotSelectedOnAllImages += (allItems - itemsOnThisImage);
-        itemsOnSomeImages += itemsOnThisImage;
+        if (firstImage) {
+            itemsOnAllImages = itemsOnThisImage;
+            firstImage = false;
+        } else {
+            foreach (const QString &item, itemsOnThisImage) {
+                if (! itemsOnAllImages.contains(item) &&
+                    ! itemsOnSomeImages.contains(item)) {
+                    itemsOnSomeImages += item;
+                }
+            }
+            foreach (const QString &item, itemsOnAllImages) {
+                if (! itemsOnThisImage.contains(item)) {
+                    itemsOnAllImages -= item;
+                }
+            }
+        }
     }
+    const StringSet itemsOnNoImages = allItems - itemsOnSomeImages - itemsOnAllImages;
 
-    const StringSet itemsOnAllImages = allItems - itemsNotSelectedOnAllImages;
-    const StringSet itemsPartiallyOn = itemsOnSomeImages - itemsOnAllImages;
-
-    return std::make_tuple(itemsOnAllImages, itemsPartiallyOn);
+    return std::make_tuple(itemsOnAllImages, itemsOnSomeImages, itemsOnNoImages);
 }
 
 void AnnotationDialog::Dialog::slotRatingChanged(unsigned int)
@@ -1383,16 +1402,23 @@ void AnnotationDialog::Dialog::saveAndClose()
             ls->slotReturn();
         }
 
+        Q_FOREACH(ListSelect *ls, m_optionList) {
+            StringSet changes = changedOptions(ls);
+            if (!(changes.isEmpty())) {
+                StringSet newItemsOn = ls->itemsOn() & changes;
+                StringSet newItemsOff = ls->itemsOff() & changes;
+                for (DB::ImageInfoListConstIterator it = m_origList.constBegin(); it != m_origList.constEnd(); ++it) {
+                    DB::ImageInfoPtr info = *it;
+                    info->addCategoryInfo(ls->category(), newItemsOn);
+                    info->removeCategoryInfo(ls->category(), newItemsOff);
+                }
+            }
+        }
         for (DB::ImageInfoListConstIterator it = m_origList.constBegin(); it != m_origList.constEnd(); ++it) {
             DB::ImageInfoPtr info = *it;
             if (!m_startDate->date().isNull())
                 info->setDate(DB::ImageDate(m_startDate->date(), m_endDate->date(), m_time->time()));
 
-            Q_FOREACH (ListSelect *ls, m_optionList) {
-                info->addCategoryInfo(ls->category(), ls->itemsOn());
-                info->removeCategoryInfo(ls->category(), ls->itemsOff());
-            }
-
             if (!m_imageLabel->text().isEmpty()) {
                 info->setLabel(m_imageLabel->text());
             }
@@ -1406,13 +1432,13 @@ void AnnotationDialog::Dialog::saveAndClose()
             }
         }
         m_ratingChanged = false;
+
     }
     m_accept = QDialog::Accepted;
 
     if (anyChanges) {
         MainWindow::DirtyIndicator::markDirty();
     }
-
     QDialog::accept();
 }
 
diff --git a/AnnotationDialog/Dialog.h b/AnnotationDialog/Dialog.h
index a40ebac9..6b6c5c40 100644
--- a/AnnotationDialog/Dialog.h
+++ b/AnnotationDialog/Dialog.h
@@ -155,6 +155,7 @@ protected:
     void showTornOfWindows();
     void hideTornOfWindows();
     bool hasChanges();
+    StringSet changedOptions(ListSelect *);
     void showHelpDialog(UsageMode);
     void resizeEvent(QResizeEvent *) override;
     void moveEvent(QMoveEvent *) override;
@@ -163,7 +164,7 @@ protected:
     void loadWindowLayout();
     void setupActions();
     void setUpCategoryListBoxForMultiImageSelection(ListSelect *, const DB::ImageInfoList &images);
-    std::tuple<Utilities::StringSet, Utilities::StringSet> selectionForMultiSelect(ListSelect *, const DB::ImageInfoList &images);
+    std::tuple<Utilities::StringSet, Utilities::StringSet, Utilities::StringSet> selectionForMultiSelect(ListSelect *, const DB::ImageInfoList &images);
     void saveAndClose();
     void ShowHideSearch(bool show);
patch-annotation-selection.patch (application/octet-stream, 1.5 KB)
diff --git a/ThumbnailView/ThumbnailWidget.cpp b/ThumbnailView/ThumbnailWidget.cpp
index 470d4da8..6ad0a696 100644
--- a/ThumbnailView/ThumbnailWidget.cpp
+++ b/ThumbnailView/ThumbnailWidget.cpp
@@ -34,6 +34,8 @@
 #include <KLocalizedString>
 #include <QScrollBar>
 #include <QTimer>
+#include <QItemSelection>
+#include <QItemSelectionRange>
 #include <math.h>
 #include <qcursor.h>
 #include <qfontmetrics.h>
@@ -410,8 +412,28 @@ void ThumbnailView::ThumbnailWidget::changeSingleSelection(const DB::FileName &f
 
 void ThumbnailView::ThumbnailWidget::select(const DB::FileNameList &items)
 {
-    Q_FOREACH (const DB::FileName &fileName, items)
-        selectionModel()->select(model()->fileNameToIndex(fileName), QItemSelectionModel::Select);
+    QItemSelection selection;
+    QModelIndex start;
+    QModelIndex end;
+    int count = 0;
+    Q_FOREACH (const DB::FileName &fileName, items) {
+        QModelIndex index = model()->fileNameToIndex(fileName);
+        if (count == 0) {
+            start = index;
+            end = index;
+        } else if (index.row() == end.row() + 1) {
+            end = index;
+        } else {
+            selection.merge(QItemSelection(start, end), QItemSelectionModel::Select);
+            start = index;
+            end = index;
+        }
+        count++;
+    }
+    if (count > 0) {
+        selection.merge(QItemSelection(start, end), QItemSelectionModel::Select);
+    }
+    selectionModel()->select(selection, QItemSelectionModel::Select);
 }
 
 bool ThumbnailView::ThumbnailWidget::isItemUnderCursorSelected() const