#####################################################################
# #
# imageoutput.py #
# #
# Copyright 2013, Monash University #
# #
# This file is part of the labscript suite (see #
# http://labscriptsuite.org) and is licensed under the Simplified #
# BSD License. See the license.txt file in the root of the project #
# for the full license. #
# #
#####################################################################
import base64
import sys
import os
from qtutils.qt.QtCore import *
from qtutils.qt.QtGui import *
from qtutils.qt.QtWidgets import *
from qtutils.qt.QtCore import pyqtSignal as Signal
[docs]class ImageView(QGraphicsView):
[docs] def __init__(self, *args, **kwargs):
QGraphicsView.__init__(self, *args, **kwargs)
self.installEventFilter(self)
# def eventFilter(self, obj, event):
# if event.type() == QEvent.ContextMenu:
# print 'a'
# menu = QMenu(self)
# menu.addAction("Lock" if self.parent().lock_state else "Unlock")
# menu.triggered.connect(self.parent()._menu_triggered)
# menu.popup(self.mapToGlobal(event.pos()))
# return True
# return QGraphicsView.eventFilter(self, obj, event)
[docs]class ImageOutput(QWidget):
imageUpdated = Signal(str)
[docs] def __init__(self, name, width, height, *args, **kwargs):
QWidget.__init__(self, *args, **kwargs)
self.setSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.Minimum)
# create the layout
self._layout = QVBoxLayout(self)
self._layout.setSpacing(0)
self._layout.setContentsMargins(0, 0, 0, 0)
# Create a layout for the header of this ImageOutput widget
header_widget = QWidget(self)
header_layout = QHBoxLayout(header_widget)
# Add the label
self._label = QLabel(name)
header_layout.addWidget(self._label)
# Add the browse button
self._browse_button = BrowseButton(self, 'Select Image')
self._browse_button.setIcon(QIcon(':/qtutils/fugue/image-import'))
header_layout.addWidget(self._browse_button)
# Add a spacer item to keep everything bunched
header_layout.addItem(QSpacerItem(0,0,QSizePolicy.MinimumExpanding,QSizePolicy.Minimum))
# add the header widget to the layout
self._layout.addWidget(header_widget)
self.image_size = QSize(width,height)
# Create the graphics scene and view
self._scene = QGraphicsScene(0, 0, width, height)
self._scene.setBackgroundBrush(Qt.black)
self._view = ImageView(self._scene)
self._view.setAlignment(Qt.AlignLeft | Qt.AlignTop)
self._view.setStyleSheet("border: 0px")
# self._view.setStyleSheet("background-color:#000000; border: 0px;")
self._view.setMinimumSize(self.image_size)
self._view.setMaximumSize(self.image_size)
self._layout.addWidget(self._view)
# Install the event filter that will allow us to catch right click mouse release events so we can popup a menu even when the button is disabled
self.installEventFilter(self)
# The Image Out object that is in charge of this button
self._Image = None
# the base64encoded value
self._value = ""
# The image item to be added to the scene
self._pixmap_item = None
# The current lock state
self.lock_state = False
# Setting and getting methods for the Image Out object in charge of this button
[docs] def set_Image(self,Image,notify_old_Image=True,notify_new_Image=True):
# If we are setting a new Image, remove this widget from the old one (if it isn't None) and add it to the new one (if it isn't None)
if Image != self._Image:
if self._Image is not None and notify_old_Image:
self._Image.remove_widget(self)
if Image is not None and notify_new_Image:
Image.add_widget(self)
# Store a reference to the Image out object
self._Image = Image
[docs] def get_Image(self):
return self._Image
# The event filter that pops up a context menu on a right click, even when the button is disabled
[docs] def eventFilter(self, obj, event):
if event.type() == QEvent.MouseButtonRelease and event.button() == Qt.RightButton:
menu = QMenu(self)
menu.addAction("Lock" if not self.lock_state else "Unlock")
menu.triggered.connect(self._menu_triggered)
menu.popup(self.mapToGlobal(event.pos()))
return True
# pass scrollwheel events of disabled buttons through to the parent
# code adapted from: http://www.qtforum.org/article/28540/disabled-widgets-and-wheel-events.html
elif obj and not obj.isEnabled() and event.type() == QEvent.Wheel:
newEvent = QWheelEvent(obj.mapToParent(event.pos()), event.globalPos(),
event.delta(), event.buttons(),
event.modifiers(), event.orientation())
QApplication.instance().postEvent(obj.parent(), newEvent)
return True
return QWidget.eventFilter(self, obj, event)
# This method is called whenever an entry in the context menu is clicked
def _menu_triggered(self,action):
if action.text() == "Lock":
self.lock()
elif action.text() == "Unlock":
self.unlock()
# This method locks (disables) the widget, and if the widget has a parent Image object, notifies it of the lock
[docs] def lock(self,notify_Image=True):
self._browse_button.setEnabled(False)
self.lock_state = True
if self._Image is not None and notify_Image:
self._Image.lock()
# This method unlocks (enables) the widget, and if the widget has a parent Image object, notifies it of the unlock
[docs] def unlock(self,notify_Image=True):
self._browse_button.setEnabled(True)
self.lock_state = False
if self._Image is not None and notify_Image:
self._Image.unlock()
@property
def value(self):
return self._value
@value.setter
def value(self, value):
if isinstance(value, bytes):
value = value.decode()
decoded_image = base64.b64decode(value)
pixmap = QPixmap()
pixmap.loadFromData(decoded_image, flags=Qt.AvoidDither | Qt.ThresholdAlphaDither | Qt.ThresholdDither)
# print decoded_image
if pixmap.size() != self.image_size:
QMessageBox.warning(self, "Failed to load image", 'The image size was incorrect. It must be %dx%d pixels.'%(self.image_size.width(), self.image_size.height()), QMessageBox.Ok, QMessageBox.Ok)
return
self._value = value
pixmap_item = QGraphicsPixmapItem(pixmap)
if self._pixmap_item is not None:
self._scene.removeItem(self._pixmap_item)
self._scene.addItem(pixmap_item)
self._pixmap_item = pixmap_item
# Tell the Image object that the value has been updated
self.imageUpdated.emit(self._value)
# A simple test!
if __name__ == '__main__':
qapplication = QApplication(sys.argv)
window = QWidget()
layout = QVBoxLayout(window)
button = ImageOutput('hello', 200, 200)
layout.addWidget(button)
window.show()
sys.exit(qapplication.exec_())