Skip to content

Commit

Permalink
Fixed #549 and refactored the image tooltip also
Browse files Browse the repository at this point in the history
Issue #549 was caused because the request and reply object urls are not
garantueed to be the same. Redirects are the most common cause, but a
malformed URL apparently also qualifies. We now make sure to look at the
original request.

Because the code confused me while I was working on it, I decided to
refactor and document it in order to understand what was going on. I am
glad I did: I found another crashing bug involving the rapid-firing of
tooltip requests, and the processing dict never had its entries removed
either, leading to a (very slow) memory leak over time.

All is good in the world of image tooltips now.
  • Loading branch information
worstje committed Apr 21, 2019
1 parent 2b4943c commit 81f59a1
Showing 1 changed file with 54 additions and 22 deletions.
76 changes: 54 additions & 22 deletions manuskript/ui/views/MDEditView.py
Original file line number Diff line number Diff line change
Expand Up @@ -527,7 +527,7 @@ def mouseMoveEvent(self, event):
+"<p><img src='data:image/png;base64,{}'></p>")
tooltip = None
pos = event.pos() + QPoint(0, ct.rect.height())
imageTooltiper.fromUrl(ct.texts[2], pos, self)
ImageTooltip.fromUrl(ct.texts[2], pos, self)

elif ct.regex == self.inlineLinkRegex:
tooltip = ct.texts[1] or ct.texts[2]
Expand Down Expand Up @@ -582,53 +582,85 @@ def __init__(self, rect, regex, texts):
from PyQt5.QtCore import QIODevice, QUrl, QBuffer
from PyQt5.QtGui import QPixmap

class imageTooltiper:
class ImageTooltip:
"""
This class handles the retrieving and caching of images in order to display these in tooltips.
"""

cache = {}
manager = QNetworkAccessManager()
data = {}
processing = {}

def fromUrl(url, pos, editor):
cache = imageTooltiper.cache
imageTooltiper.editor = editor
"""
Shows the image tooltip for the given url if available, or requests it for future use.
"""
ImageTooltip.editor = editor

if url in cache:
if not cache[url][0]: # error, image was not found
imageTooltiper.tooltipError(cache[url][1], pos)
else:
imageTooltiper.tooltip(cache[url][1], pos)
return
if ImageTooltip.showTooltip(url, pos):
return # the url already exists in the cache

try:
imageTooltiper.manager.finished.connect(imageTooltiper.finished, F.AUC)
ImageTooltip.manager.finished.connect(ImageTooltip.finished, F.AUC)
except:
pass
pass # already connected

qurl = QUrl(url)
if (qurl in ImageTooltip.processing):
return # one download is more than enough

request = QNetworkRequest(QUrl(url))
imageTooltiper.data[QUrl(url)] = (pos, url)
imageTooltiper.manager.get(request)
# Request the image for later processing.
request = QNetworkRequest(qurl)
ImageTooltip.processing[qurl] = (pos, url)
ImageTooltip.manager.get(request)

def finished(reply):
cache = imageTooltiper.cache
pos, url = imageTooltiper.data[reply.url()]
"""
After retrieving an image, we add it to the cache.
"""
cache = ImageTooltip.cache

# Update cache with retrieved data.
pos, url = ImageTooltip.processing[reply.request().url()]
if reply.error() != QNetworkReply.NoError:
cache[url] = (False, reply.errorString())
imageTooltiper.tooltipError(reply.errorString(), pos)
else:
px = QPixmap()
px.loadFromData(reply.readAll())
px = px.scaled(800, 600, Qt.KeepAspectRatio)
cache[url] = (True, px)
imageTooltiper.tooltip(px, pos)
del ImageTooltip.processing[reply.request().url()]

ImageTooltip.showTooltip(url, pos)

def showTooltip(url, pos):
"""
Show a tooltip for the given url based on cached information.
"""
cache = ImageTooltip.cache

if url in cache:
if not cache[url][0]: # error, image was not found
ImageTooltip.tooltipError(cache[url][1], pos)
else:
ImageTooltip.tooltip(cache[url][1], pos)
return True
return False

def tooltipError(message, pos):
imageTooltiper.editor.doTooltip(pos, message)
"""
Display a tooltip with an error message at the given position.
"""
ImageTooltip.editor.doTooltip(pos, message)

def tooltip(image, pos):
"""
Display a tooltip with an image at the given position.
"""
px = image
buffer = QBuffer()
buffer.open(QIODevice.WriteOnly)
px.save(buffer, "PNG", quality=100)
image = bytes(buffer.data().toBase64()).decode()
tt = "<p><img src='data:image/png;base64,{}'></p>".format(image)
imageTooltiper.editor.doTooltip(pos, tt)
ImageTooltip.editor.doTooltip(pos, tt)

0 comments on commit 81f59a1

Please sign in to comment.