mirror of
https://github.com/subsurface/subsurface.git
synced 2025-02-19 22:16:15 +00:00
Profile: add "synchronous" mode for picture plotting
The thumbnails were fetched in the background to achieve a snappier UI. The problem with that is that on LaTeX etc. export only placeholder thumbnails were shown. Therefore, implement a synchronous mode. This only tries to fetch cached thumbnails or calculate thumbnails for images. Videos and remote files are not supported. Fixes #1963 Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
This commit is contained in:
parent
07b0df215f
commit
8a8063c1cd
6 changed files with 40 additions and 15 deletions
|
@ -456,8 +456,23 @@ void Thumbnailer::imageDownloadFailed(QString filename)
|
||||||
workingOn.remove(filename);
|
workingOn.remove(filename);
|
||||||
}
|
}
|
||||||
|
|
||||||
QImage Thumbnailer::fetchThumbnail(const QString &filename)
|
QImage Thumbnailer::fetchThumbnail(const QString &filename, bool synchronous)
|
||||||
{
|
{
|
||||||
|
if (synchronous) {
|
||||||
|
// In synchronous mode, first try the thumbnail cache.
|
||||||
|
Thumbnail thumbnail = getThumbnailFromCache(filename);
|
||||||
|
if (!thumbnail.img.isNull())
|
||||||
|
return thumbnail.img;
|
||||||
|
|
||||||
|
// If that didn't work, try to thumbnail the image.
|
||||||
|
thumbnail = getHashedImage(filename, false);
|
||||||
|
if (thumbnail.type == MEDIATYPE_STILL_LOADING || thumbnail.img.isNull())
|
||||||
|
return failImage; // No support for delayed thumbnails (web).
|
||||||
|
|
||||||
|
int size = maxThumbnailSize();
|
||||||
|
return thumbnail.img.scaled(size, size, Qt::KeepAspectRatio);
|
||||||
|
}
|
||||||
|
|
||||||
QMutexLocker l(&lock);
|
QMutexLocker l(&lock);
|
||||||
|
|
||||||
// We are not currently fetching this thumbnail - add it to the list.
|
// We are not currently fetching this thumbnail - add it to the list.
|
||||||
|
|
|
@ -31,9 +31,13 @@ public:
|
||||||
static Thumbnailer *instance();
|
static Thumbnailer *instance();
|
||||||
|
|
||||||
// Schedule a thumbnail for fetching or calculation.
|
// Schedule a thumbnail for fetching or calculation.
|
||||||
// Returns a placeholder thumbnail. The actual thumbnail will be sent
|
// If synchronous is false, returns a placeholder thumbnail.
|
||||||
// via a signal later.
|
// The actual thumbnail will be sent via a signal later.
|
||||||
QImage fetchThumbnail(const QString &filename);
|
// If synchronous is true, try to fetch the actual thumbnail.
|
||||||
|
// In this mode only precalculated thumbnails or thumbnails
|
||||||
|
// from pictures are returned. Video extraction and remote
|
||||||
|
// images are not supported.
|
||||||
|
QImage fetchThumbnail(const QString &filename, bool synchronous);
|
||||||
|
|
||||||
// Schedule multiple thumbnails for forced recalculation
|
// Schedule multiple thumbnails for forced recalculation
|
||||||
void calculateThumbnails(const QVector<QString> &filenames);
|
void calculateThumbnails(const QVector<QString> &filenames);
|
||||||
|
|
|
@ -254,7 +254,7 @@ void DiveLogExportDialog::exportProfile(const QString filename, const bool selec
|
||||||
void DiveLogExportDialog::saveProfile(const struct dive *dive, const QString filename)
|
void DiveLogExportDialog::saveProfile(const struct dive *dive, const QString filename)
|
||||||
{
|
{
|
||||||
ProfileWidget2 *profile = MainWindow::instance()->graphics;
|
ProfileWidget2 *profile = MainWindow::instance()->graphics;
|
||||||
profile->plotDive(dive, true);
|
profile->plotDive(dive, true, false, true);
|
||||||
profile->setToolTipVisibile(false);
|
profile->setToolTipVisibile(false);
|
||||||
QPixmap pix = profile->grab();
|
QPixmap pix = profile->grab();
|
||||||
profile->setToolTipVisibile(true);
|
profile->setToolTipVisibile(true);
|
||||||
|
|
|
@ -165,7 +165,7 @@ ProfileWidget2::ProfileWidget2(QWidget *parent) : QGraphicsView(parent),
|
||||||
addActionShortcut(Qt::Key_Right, &ProfileWidget2::keyRightAction);
|
addActionShortcut(Qt::Key_Right, &ProfileWidget2::keyRightAction);
|
||||||
|
|
||||||
connect(Thumbnailer::instance(), &Thumbnailer::thumbnailChanged, this, &ProfileWidget2::updateThumbnail, Qt::QueuedConnection);
|
connect(Thumbnailer::instance(), &Thumbnailer::thumbnailChanged, this, &ProfileWidget2::updateThumbnail, Qt::QueuedConnection);
|
||||||
connect(DivePictureModel::instance(), SIGNAL(rowsInserted(const QModelIndex &, int, int)), this, SLOT(plotPictures()));
|
connect(DivePictureModel::instance(), &DivePictureModel::rowsInserted, this, &ProfileWidget2::plotPictures);
|
||||||
connect(DivePictureModel::instance(), &DivePictureModel::picturesRemoved, this, &ProfileWidget2::removePictures);
|
connect(DivePictureModel::instance(), &DivePictureModel::picturesRemoved, this, &ProfileWidget2::removePictures);
|
||||||
connect(DivePictureModel::instance(), &DivePictureModel::modelReset, this, &ProfileWidget2::plotPictures);
|
connect(DivePictureModel::instance(), &DivePictureModel::modelReset, this, &ProfileWidget2::plotPictures);
|
||||||
#endif // SUBSURFACE_MOBILE
|
#endif // SUBSURFACE_MOBILE
|
||||||
|
@ -548,7 +548,7 @@ void ProfileWidget2::resetZoom()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Currently just one dive, but the plan is to enable All of the selected dives.
|
// Currently just one dive, but the plan is to enable All of the selected dives.
|
||||||
void ProfileWidget2::plotDive(const struct dive *d, bool force, bool doClearPictures)
|
void ProfileWidget2::plotDive(const struct dive *d, bool force, bool doClearPictures, bool plotPicturesSynchronously)
|
||||||
{
|
{
|
||||||
static bool firstCall = true;
|
static bool firstCall = true;
|
||||||
#ifndef SUBSURFACE_MOBILE
|
#ifndef SUBSURFACE_MOBILE
|
||||||
|
@ -827,7 +827,7 @@ void ProfileWidget2::plotDive(const struct dive *d, bool force, bool doClearPict
|
||||||
if (doClearPictures)
|
if (doClearPictures)
|
||||||
clearPictures();
|
clearPictures();
|
||||||
else
|
else
|
||||||
plotPictures();
|
plotPicturesInternal(plotPicturesSynchronously);
|
||||||
|
|
||||||
toolTipItem->refresh(mapToScene(mapFromGlobal(QCursor::pos())));
|
toolTipItem->refresh(mapToScene(mapFromGlobal(QCursor::pos())));
|
||||||
#endif
|
#endif
|
||||||
|
@ -2153,7 +2153,7 @@ void ProfileWidget2::updateThumbnail(QString filename, QImage thumbnail, duratio
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create a PictureEntry object and add its thumbnail to the scene if profile pictures are shown.
|
// Create a PictureEntry object and add its thumbnail to the scene if profile pictures are shown.
|
||||||
ProfileWidget2::PictureEntry::PictureEntry(offset_t offsetIn, const QString &filenameIn, QGraphicsScene *scene) : offset(offsetIn),
|
ProfileWidget2::PictureEntry::PictureEntry(offset_t offsetIn, const QString &filenameIn, QGraphicsScene *scene, bool synchronous) : offset(offsetIn),
|
||||||
duration(duration_t {0}),
|
duration(duration_t {0}),
|
||||||
filename(filenameIn),
|
filename(filenameIn),
|
||||||
thumbnail(new DivePictureItem)
|
thumbnail(new DivePictureItem)
|
||||||
|
@ -2161,7 +2161,7 @@ ProfileWidget2::PictureEntry::PictureEntry(offset_t offsetIn, const QString &fil
|
||||||
int size = Thumbnailer::defaultThumbnailSize();
|
int size = Thumbnailer::defaultThumbnailSize();
|
||||||
scene->addItem(thumbnail.get());
|
scene->addItem(thumbnail.get());
|
||||||
thumbnail->setVisible(prefs.show_pictures_in_profile);
|
thumbnail->setVisible(prefs.show_pictures_in_profile);
|
||||||
QImage img = Thumbnailer::instance()->fetchThumbnail(filename).scaled(size, size, Qt::KeepAspectRatio);
|
QImage img = Thumbnailer::instance()->fetchThumbnail(filename, synchronous).scaled(size, size, Qt::KeepAspectRatio);
|
||||||
thumbnail->setPixmap(QPixmap::fromImage(img));
|
thumbnail->setPixmap(QPixmap::fromImage(img));
|
||||||
thumbnail->setFileUrl(filename);
|
thumbnail->setFileUrl(filename);
|
||||||
}
|
}
|
||||||
|
@ -2227,6 +2227,11 @@ void ProfileWidget2::updateThumbnailXPos(PictureEntry &e)
|
||||||
|
|
||||||
// This function resets the picture thumbnails of the current dive.
|
// This function resets the picture thumbnails of the current dive.
|
||||||
void ProfileWidget2::plotPictures()
|
void ProfileWidget2::plotPictures()
|
||||||
|
{
|
||||||
|
plotPicturesInternal(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ProfileWidget2::plotPicturesInternal(bool synchronous)
|
||||||
{
|
{
|
||||||
pictures.clear();
|
pictures.clear();
|
||||||
if (currentState == ADD || currentState == PLAN)
|
if (currentState == ADD || currentState == PLAN)
|
||||||
|
@ -2238,7 +2243,7 @@ void ProfileWidget2::plotPictures()
|
||||||
// Note that FOR_EACH_PICTURE handles current_dive being null gracefully.
|
// Note that FOR_EACH_PICTURE handles current_dive being null gracefully.
|
||||||
FOR_EACH_PICTURE(current_dive) {
|
FOR_EACH_PICTURE(current_dive) {
|
||||||
if (picture->offset.seconds > 0 && picture->offset.seconds <= current_dive->duration.seconds)
|
if (picture->offset.seconds > 0 && picture->offset.seconds <= current_dive->duration.seconds)
|
||||||
pictures.emplace_back(picture->offset, QString(picture->filename), scene());
|
pictures.emplace_back(picture->offset, QString(picture->filename), scene(), synchronous);
|
||||||
}
|
}
|
||||||
if (pictures.empty())
|
if (pictures.empty())
|
||||||
return;
|
return;
|
||||||
|
@ -2346,7 +2351,7 @@ void ProfileWidget2::dropEvent(QDropEvent *event)
|
||||||
// The parameters are passed directly to the contructor.
|
// The parameters are passed directly to the contructor.
|
||||||
// The call returns an iterator to the new element (which might differ from
|
// The call returns an iterator to the new element (which might differ from
|
||||||
// the old iterator, since the buffer might have been reallocated).
|
// the old iterator, since the buffer might have been reallocated).
|
||||||
newPos = pictures.emplace(newPos, offset, filename, scene());
|
newPos = pictures.emplace(newPos, offset, filename, scene(), false);
|
||||||
updateThumbnailXPos(*newPos);
|
updateThumbnailXPos(*newPos);
|
||||||
calculatePictureYPositions();
|
calculatePictureYPositions();
|
||||||
}
|
}
|
||||||
|
|
|
@ -75,7 +75,7 @@ public:
|
||||||
ProfileWidget2(QWidget *parent = 0);
|
ProfileWidget2(QWidget *parent = 0);
|
||||||
void resetZoom();
|
void resetZoom();
|
||||||
void scale(qreal sx, qreal sy);
|
void scale(qreal sx, qreal sy);
|
||||||
void plotDive(const struct dive *d = 0, bool force = false, bool clearPictures = false);
|
void plotDive(const struct dive *d = 0, bool force = false, bool clearPictures = false, bool plotPicturesSynchronously = false);
|
||||||
void setupItem(AbstractProfilePolygonItem *item, DiveCartesianAxis *vAxis, int vData, int hData, int zValue);
|
void setupItem(AbstractProfilePolygonItem *item, DiveCartesianAxis *vAxis, int vData, int hData, int zValue);
|
||||||
void setPrintMode(bool mode, bool grayscale = false);
|
void setPrintMode(bool mode, bool grayscale = false);
|
||||||
bool getPrintMode();
|
bool getPrintMode();
|
||||||
|
@ -173,6 +173,7 @@ private: /*methods*/
|
||||||
void createPPGas(PartialPressureGasItem *item, int verticalColumn, color_index_t color, color_index_t colorAlert,
|
void createPPGas(PartialPressureGasItem *item, int verticalColumn, color_index_t color, color_index_t colorAlert,
|
||||||
const double *thresholdSettingsMin, const double *thresholdSettingsMax);
|
const double *thresholdSettingsMin, const double *thresholdSettingsMax);
|
||||||
void clearPictures();
|
void clearPictures();
|
||||||
|
void plotPicturesInternal(bool synchronous);
|
||||||
private:
|
private:
|
||||||
DivePlotDataModel *dataModel;
|
DivePlotDataModel *dataModel;
|
||||||
int zoomLevel;
|
int zoomLevel;
|
||||||
|
@ -240,7 +241,7 @@ private:
|
||||||
std::unique_ptr<DivePictureItem> thumbnail;
|
std::unique_ptr<DivePictureItem> thumbnail;
|
||||||
// For videos with known duration, we represent the duration of the video by a line
|
// For videos with known duration, we represent the duration of the video by a line
|
||||||
std::unique_ptr<QGraphicsRectItem> durationLine;
|
std::unique_ptr<QGraphicsRectItem> durationLine;
|
||||||
PictureEntry (offset_t offsetIn, const QString &filenameIn, QGraphicsScene *scene);
|
PictureEntry (offset_t offsetIn, const QString &filenameIn, QGraphicsScene *scene, bool synchronous);
|
||||||
bool operator< (const PictureEntry &e) const;
|
bool operator< (const PictureEntry &e) const;
|
||||||
};
|
};
|
||||||
void updateThumbnailXPos(PictureEntry &e);
|
void updateThumbnailXPos(PictureEntry &e);
|
||||||
|
|
|
@ -42,7 +42,7 @@ void DivePictureModel::updateThumbnails()
|
||||||
{
|
{
|
||||||
updateZoom();
|
updateZoom();
|
||||||
for (PictureEntry &entry: pictures)
|
for (PictureEntry &entry: pictures)
|
||||||
entry.image = Thumbnailer::instance()->fetchThumbnail(entry.filename);
|
entry.image = Thumbnailer::instance()->fetchThumbnail(entry.filename, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
void DivePictureModel::updateDivePictures()
|
void DivePictureModel::updateDivePictures()
|
||||||
|
|
Loading…
Add table
Reference in a new issue