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 ofQString
s 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)
}
}