Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
160 changes: 83 additions & 77 deletions Orange/widgets/data/owimageviewer.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
-------------------

"""
import sys
import os
import weakref
import logging
Expand Down Expand Up @@ -43,6 +44,7 @@ def __init__(self, pixmap, parent=None):
self.setCacheMode(QGraphicsItem.ItemCoordinateCache)
self._pixmap = pixmap
self._pixmapSize = QSizeF()
self._keepAspect = True
self.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)

def setPixmap(self, pixmap):
Expand All @@ -53,30 +55,29 @@ def setPixmap(self, pixmap):
def pixmap(self):
return QPixmap(self._pixmap)

def setPixmapSize(self, size):
if self._pixmapSize != size:
self._pixmapSize = QSizeF(size)
self.updateGeometry()

def pixmapSize(self):
if self._pixmapSize.isValid():
return QSizeF(self._pixmapSize)
else:
return QSizeF(self._pixmap.size())

def sizeHint(self, which, constraint=QSizeF()):
if which == Qt.PreferredSize:
return self.pixmapSize()
return QSizeF(self._pixmap.size())
else:
return QGraphicsWidget.sizeHint(self, which, constraint)

def setKeepAspectRatio(self, keep):
if self._keepAspect != keep:
self._keepAspect = bool(keep)
self.update()

def keepAspectRatio(self):
return self._keepAspect

def paint(self, painter, option, widget=0):
if self._pixmap.isNull():
return

rect = self.contentsRect()

pixsize = self.pixmapSize()
pixsize = QSizeF(self._pixmap.size())
aspectmode = (Qt.KeepAspectRatio if self._keepAspect
else Qt.IgnoreAspectRatio)
pixsize.scale(rect.size(), aspectmode)
pixrect = QRectF(QPointF(0, 0), pixsize)
pixrect.moveCenter(rect.center())

Expand Down Expand Up @@ -132,13 +133,13 @@ def __init__(self, pixmap, title="", parent=None):

layout.addItem(self.pixmapWidget)
layout.addItem(self.labelWidget)

layout.addStretch()
layout.setAlignment(self.pixmapWidget, Qt.AlignCenter)
layout.setAlignment(self.labelWidget, Qt.AlignHCenter | Qt.AlignBottom)

self.setLayout(layout)

self.setSizePolicy(QSizePolicy.MinimumExpanding,
QSizePolicy.MinimumExpanding)
self.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Minimum)

self.setFlag(QGraphicsItem.ItemIsSelectable, True)
self.setTitle(title)
Expand Down Expand Up @@ -178,18 +179,14 @@ def paint(self, painter, option, widget=0):
painter.save()
painter.setPen(QPen(QColor(125, 162, 206, 192)))
painter.setBrush(QBrush(QColor(217, 232, 252, 192)))
painter.drawRoundedRect(QRectF(contents.topLeft(),
self.geometry().size()), 3, 3)
painter.drawRoundedRect(
QRectF(contents.topLeft(), self.geometry().size()), 3, 3)
painter.restore()

def _updatePixmapSize(self):
pixmap = self.pixmap()
if not pixmap.isNull() and self._size.isValid():
pixsize = QSizeF(self.pixmap().size())
pixsize.scale(self._size, Qt.KeepAspectRatio)
else:
pixsize = QSizeF()
self.pixmapWidget.setPixmapSize(pixsize)
pixsize = QSizeF(self._size)
self.pixmapWidget.setMinimumSize(pixsize)
self.pixmapWidget.setMaximumSize(pixsize)


class ThumbnailWidget(QGraphicsWidget):
Expand All @@ -206,6 +203,16 @@ def setGeometry(self, geom):
super(ThumbnailWidget, self).setGeometry(geom)
self.reflow(self.size().width())

def event(self, event):
if event.type() == QEvent.LayoutRequest:
sh = self.effectiveSizeHint(Qt.PreferredSize)
self.resize(sh)
self.layout().activate()
event.accept()
return True
else:
return super().event(event)

def reflow(self, width):
if not self.layout():
return
Expand All @@ -231,6 +238,8 @@ def reflow(self, width):
for i, item in enumerate(items):
layout.addItem(item, i // ncol, i % ncol)

layout.invalidate()

def items(self):
layout = self.layout()
if layout:
Expand Down Expand Up @@ -330,14 +339,27 @@ class OWImageViewer(widget.OWWidget):
imageAttr = settings.ContextSetting(0)
titleAttr = settings.ContextSetting(0)

zoom = settings.Setting(25)
imageSize = settings.Setting(100)
autoCommit = settings.Setting(False)

buttons_area_orientation = Qt.Vertical
graph_name = "scene"

def __init__(self):
super().__init__()
self.data = None
self.allAttrs = []
self.stringAttrs = []

self.thumbnailWidget = None
self.sceneLayout = None
self.selectedIndices = []

#: List of _ImageItems
self.items = []

self._errcount = 0
self._successcount = 0

self.info = gui.widgetLabel(
gui.vBox(self.controlArea, "Info"),
Expand All @@ -363,9 +385,9 @@ def __init__(self):
)

gui.hSlider(
self.controlArea, self, "zoom",
box="Zoom", minValue=1, maxValue=100, step=1,
callback=self.updateZoom,
self.controlArea, self, "imageSize",
box="Image Size", minValue=32, maxValue=1024, step=16,
callback=self.updateSize,
createLabel=False
)
gui.rubber(self.controlArea)
Expand All @@ -388,16 +410,6 @@ def __init__(self):
)
self.resize(800, 600)

self.thumbnailWidget = None
self.sceneLayout = None
self.selectedIndices = []

#: List of _ImageItems
self.items = []

self._errcount = 0
self._successcount = 0

self.loader = ImageLoader(self)

def setData(self, data):
Expand Down Expand Up @@ -452,7 +464,7 @@ def setupScene(self):
if numpy.isfinite(inst[attr])]
widget = ThumbnailWidget()
layout = widget.layout()

size = QSizeF(self.imageSize, self.imageSize)
self.scene.addItem(widget)

for i, inst in enumerate(instances):
Expand All @@ -462,7 +474,7 @@ def setupScene(self):
thumbnail = GraphicsThumbnailWidget(
QPixmap(), title=title, parent=widget
)

thumbnail.setThumbnailSize(size)
thumbnail.setToolTip(url.toString())
thumbnail.instance = inst
layout.addItem(thumbnail, i / 5, i % 5)
Expand All @@ -486,8 +498,6 @@ def set_pixmap(future, thumb=thumbnail):
pixmap = QPixmap.fromImage(future.result())

thumb.setPixmap(pixmap)
if not pixmap.isNull():
thumb.setThumbnailSize(self.pixmapSize(pixmap))

self._updateStatus(future)

Expand All @@ -503,11 +513,7 @@ def set_pixmap(future, thumb=thumbnail):
self.sceneLayout = layout

if self.sceneLayout:
width = (self.sceneView.width() -
self.sceneView.verticalScrollBar().width())
self.thumbnailWidget.reflow(width)
self.thumbnailWidget.setPreferredWidth(width)
self.sceneLayout.activate()
self._updateGeometryConstraints()

def urlFromValue(self, value):
variable = value.variable
Expand All @@ -530,14 +536,6 @@ def urlFromValue(self, value):
url.setScheme("file")
return url

def pixmapSize(self, pixmap):
"""
Return the preferred pixmap size based on the current `zoom` value.
"""
scale = 2 * self.zoom / 100.0
size = QSizeF(pixmap.size()) * scale
return size.expandedTo(QSizeF(16, 16))

def _cancelAllFutures(self):
for item in self.items:
if item.future is not None:
Expand All @@ -560,19 +558,12 @@ def clearScene(self):
def thumbnailItems(self):
return [item.widget for item in self.items]

def updateZoom(self):
def updateSize(self):
size = QSizeF(self.imageSize, self.imageSize)
for item in self.thumbnailItems():
item.setThumbnailSize(self.pixmapSize(item.pixmap()))

if self.thumbnailWidget:
width = (self.sceneView.width() -
self.sceneView.verticalScrollBar().width())
item.setThumbnailSize(size)

self.thumbnailWidget.reflow(width)
self.thumbnailWidget.setPreferredWidth(width)

if self.sceneLayout:
self.sceneLayout.activate()
self._updateGeometryConstraints()

def updateTitles(self):
titleAttr = self.allAttrs[self.titleAttr]
Expand Down Expand Up @@ -632,24 +623,32 @@ def _updateStatus(self, future):
def _updateSceneRect(self):
self.scene.setSceneRect(self.scene.itemsBoundingRect())

def _updateGeometryConstraints(self):
# Update the thumbnail grid widget's width constraint (derived from
# the viewport's width
if self.thumbnailWidget is not None:
width = (self.sceneView.width() -
self.sceneView.verticalScrollBar().width())
width = width - 2
self.thumbnailWidget.reflow(width)
self.thumbnailWidget.setPreferredWidth(width)
self.thumbnailWidget.layout().activate()

def onDeleteWidget(self):
self._cancelAllFutures()
self.clear()

def eventFilter(self, receiver, event):
if receiver is self.sceneView and event.type() == QEvent.Resize \
and self.thumbnailWidget:
width = (self.sceneView.width() -
self.sceneView.verticalScrollBar().width())

self.thumbnailWidget.reflow(width)
self.thumbnailWidget.setPreferredWidth(width)
self._updateGeometryConstraints()

return super(OWImageViewer, self).eventFilter(receiver, event)


class ImageLoader(QObject):
#: A weakref to a QNetworkAccessManager used for image retrieval.
#: (we can only have only one QNetworkDiskCache opened on the same
#: (we can only have one QNetworkDiskCache opened on the same
#: directory)
_NETMANAGER_REF = None

Expand Down Expand Up @@ -740,17 +739,24 @@ def on_reply_ready(reply, future):
return future


def main():
def main(argv=sys.argv):
import sip

app = QApplication([])
app = QApplication(argv)
argv = app.arguments()
w = OWImageViewer()
w.show()
w.raise_()
data = Orange.data.Table('zoo-with-images')

if len(argv) > 1:
data = Orange.data.Table(argv[1])
else:
data = Orange.data.Table('zoo-with-images')

w.setData(data)
rval = app.exec_()
w.saveSettings()
w.onDeleteWidget()
sip.delete(w)
app.processEvents()
return rval
Expand Down