Confusion with images shown in gridview and listview (also using Nemo Thumbnailer)

I have an app which:

  • shows images in a gridview
  • each cell shows a thumbnail generated by Nemo Thumbnailer
  • when tapping a cell, a dedicated page showing the fullscreen image opens
  • this page contains a listview so that images can be scrolled (horizontally)
  • the underlying model in both cases is a subclass of QAbstractItemModel which basically just keeps a list of QStrings holding the filesystem path to the image
  • a single instance of the model instance is actually used by both pages

Now in addition to displaying images, which works fine, the fullscreen image page (with the listview) allows the user to edit images. The images can be saved back to the original file, overwriting it.

While it basically works, I have issues with updating both the listview (currently displayed fullscreen image) as well as the grid view (overview of all images via Nemo Thumbnailer).

In addition to what was said, I have 2 distinct approaches of saving images:

  • via QML / Item::grabToImage
  • via C++

In both cases, when overwriting an image, the model data won’t change, as the filesystem path does not change. Only the contents of the file itself will change, which is not part of the model.

I am trying to make
a) the fullscreen edit page to refresh in order to display the edited image
b) the gridview update the thumbnail of the edited image in order to show an updated thumbnail

I have gotten a) to work with a combination of Image::sourceChanged in QML and beginResetModel() / endResetModel() in C++. For some reason I do not understand it does not suffice if only using either the QML or the C++ part. While it works, I would rather avoid resetting the model, as I am afraid this could introduce performance issues on large model data, however it only seems to work like this (e.g. emitting dataChanged from C++ does not help either).

For b), I fail to get the thumbnails to update. Those seem to update only if I restart the app, and there seems to be no way to trigger Nemo Thumbnailer to generate a new thumbnail for a given image.

How would I work around this?

Some parts of the code (overview gridview (thumbnailer), fullscreen image page listview, model functions, fullscreen image page save function):

   SilicaGridView {
      id: grid
      cellWidth: width / 3
      cellHeight: width / 3
      anchors.fill: parent

      model: imageModel

      delegate: GridItem {
         Image {
            id: image
            anchors.fill: parent
            asynchronous: true
            source:  "image://nemoThumbnail/" + model.path
            sourceSize.width: grid.cellWidth
            sourceSize.height: grid.cellHeight
         }
[...]
   SilicaListView {
      id: listView
      anchors.fill: parent
      clip: true
      snapMode: ListView.SnapOneItem
      orientation: ListView.HorizontalFlick
      highlightRangeMode: ListView.StrictlyEnforceRange
      cacheBuffer: width

      model: imageModel

      delegate: Item {
         width: listView.width
         height: listView.height
         clip: true

         Image {
            z: 1
            id: image
            anchors.horizontalCenter: parent.horizontalCenter
            anchors.verticalCenter: parent.verticalCenter
            source: model.path
            sourceSize.width: parent.width
            fillMode: Image.PreserveAspectFit
            visible: mode !== ImageUtils.modeContrast && mode !== ImageUtils.modeHue
            autoTransform: false
            asynchronous: true
            cache: false
[...]
QString ImageModel::rotateImage(const QString& path, double angle, const QString& targetPath)
{
   auto it = std::find_if(mItems.begin(), mItems.end(), [&path](const ImageItem& x) { return x.path == path; });

   if (it == mItems.end())
      return tr("Image not found");

   QImage image(path);
   QImage editedImage = image.transformed(QMatrix().rotate(angle));

   bool res = editedImage.save(targetPath);

   if (!res)
      return tr("Image saving failed");

   updateImageData(it - mItems.begin());
   return "";
}

void ImageModel::updateImageData(int theIndex)
{
//   emit dataChanged(index(theIndex, 0), index(theIndex, 0));  // doesnt work anyway
   beginResetModel();
   endResetModel();
}
EditButton {
   pIcon: "image://theme/icon-m-accept"
   pClicked: function() {
      var dialog = pageStack.push(Qt.resolvedUrl("../dialogs/ImageSaveDialog.qml") {...})

      var saveImageFn = function(result, targetPath) {
         result.saveToFile(targetPath)

         imageModel.updateImageData(listView.currentIndex) // this resets the model
         image.sourceChanged(image.source);
      }

      var acceptFn = function() {
         var targetPath = dialog.overwrite ? model.path : dialog.newFilePath

         if (mode === ImageUtils.modeRotate)
            imageModel.rotateImage(model.path, imageRotation.angle, targetPath)
         else
            imageContrastHidden.grabToImage(function(res) { saveImageFn(res, targetPath); });
      }

      dialog.accepted.connect(acceptFn)
   }
}

Maybe you tried a complicate story. Hopefully in the next days my app “Ielig Image” come in the store. You can find the QML code after installing in /usr/share/harbour-ieligimage/qml… Maybe this example can answer your question. If you need more help, you can find my mail-addr under “Help” in the app.

Happy hacking

So I have a general misconception here? Can you elaborate?

Here in this forum I have only some hints:

  • Why ListView if you only want a scrollable area. Take Sailifishflickable and set the contentheigth to the image size. Thats enough. You will also need a PinchArea.

  • The GridView is based on URLs to the images? So for GridView nothing changes. You have to update the model. The QT-help discuss for external changes to the model data the function sync(). But I have never used this function, in my cases “model.clear” and “model.append” will to the trick.

  • the Nemo thumbmailer is a cache. Have you look for a function to refresh this cache? Maybe in total it is a good idea to rename the pic file name, e.g. with adding a timestamp. So you can also create a extended UNDO. And check the cache settings in GridView, thats not always needed. In QT-help is a discussion about GridView and performance with comments to the cache.

Maybe you wait for the app I mention above. The app is still waiting for QA approve. Then we can discuss more in detail per mail or so.

I’ve only shown an excerpt of the code. There is PinchArea and all the stuff. And I think for the issues I have described, SailfishFlickable vs. Listview won’t make much of a difference.

No the GridView is fed by the thumbnailer (see first code snippet. model.path = the full filesystem URL to the image). As I said, the model only contains URLs, which don’t change, so nothing would have to be updated. I am, nevertheless, emitting dataChanged signals / resetting the model in order to have the ListView display refresh, as I stated in the original post.

Yeah well, this is actually exactly my question. I know its a cache. The question was how I would be able to refresh it?
I don’t want to save it under a different filename, because the functionality which is to be implemented is about overwriting the file. I have the option to save it under a different name also, and this funtion works without issues. So this topic is about overwriting the files.