Files
scikit-image/scikits/image/io/_plugins/qt_plugin.py
T
2009-11-04 19:53:42 +01:00

400 lines
15 KiB
Python

from util import prepare_for_display, window_manager, GuiLockError, ColorMixer
import numpy as np
import sys
try:
# We try to aquire the gui lock first or else the gui import might
# trample another GUI's PyOS_InputHook.
window_manager.acquire('qt')
except GuiLockError, gle:
print gle
else:
try:
from PyQt4.QtGui import (QApplication, QMainWindow, QImage, QPixmap,
QLabel, QWidget, QVBoxLayout, QSlider)
from PyQt4 import QtCore, QtGui
except ImportError:
print 'PyQT4 libraries not installed. Plugin not loaded.'
window_manager._release('qt')
else:
app = None
class LabelImage(QLabel):
def __init__(self, parent, arr):
QLabel.__init__(self)
self.parent = parent
# we need to hold a reference to
# arr because QImage doesn't copy the data
# and the buffer must be alive as long
# as the image is alive.
self.arr = arr
# we also need to pass in the row-stride to
# the constructor, because we can't guarantee
# that every row of the numpy data is
# 4-byte aligned. Which Qt would require
# if we didnt pass the stride.
self.img = QImage(arr.data, arr.shape[1], arr.shape[0],
arr.strides[0], QImage.Format_RGB888)
self.pm = QPixmap.fromImage(self.img)
self.setPixmap(self.pm)
def mouseMoveEvent(self, evt):
self.parent.label_mouseMoveEvent(evt)
class ImageWindow(QMainWindow):
def __init__(self, arr, mgr):
QMainWindow.__init__(self)
self.mgr = mgr
self.main_widget = QWidget()
self.layout = QtGui.QHBoxLayout(self.main_widget)
self.setCentralWidget(self.main_widget)
self.label = LabelImage(self, arr)
self.layout.addWidget(self.label)
self.mgr.add_window(self)
self.main_widget.show()
def closeEvent(self, event):
# Allow window to be destroyed by removing any
# references to it
self.mgr.remove_window(self)
def label_mouseMoveEvent(self, evt):
pass
class IntelligentSlider(QSlider):
''' A slider that adds a 'name' attribute and calls a callback
with 'name' as an argument to the registerd callback.
This allows you to create large groups of sliders in a loop,
but still keep track of the individual events'''
def __init__(self, name, callback):
QSlider.__init__(self)
self.name = name
self.callback = callback
# bind this to the valueChanged signal of the slider
def i_changed(self, val):
self.callback(self.name, val)
class NSliderBlock(QWidget):
'''Creates a block of n sliders with ranges
specified as a list of tuples. The fourth entry
in the tuple will be used as a dictionary key
so you can access the slider later.
So the tuple should be (min, max, initial, name)
The callback is the function to be called
when a slider value changes. The callback will be
called with the following arguments (name, value).
You can get a hook to a specific slider using
NSliderBlock.sliders[name]
'''
def __init__(self, n, ranges_labels, callback):
QWidget.__init__(self)
if len(ranges_labels) != n:
raise ValueError('not enough or too many ranges supplied')
self.callback = callback
self.sliders = {}
self.slider_names = []
self.layout = QtGui.QGridLayout(self)
for i in range(n):
params = ranges_labels[i]
if len(params) != 4:
raise ValueError('Tuples must be length 4')
name = params[3]
slider_name = QLabel()
slider_name.setText(name)
slider_name.setAlignment(QtCore.Qt.AlignCenter)
self.slider_names.append(slider_name)
self.layout.addWidget(slider_name, 0, i)
slider = IntelligentSlider(name, self.callback)
slider.setMinimum(params[0])
slider.setMaximum(params[1])
slider.setValue(params[2])
slider.valueChanged.connect(slider.i_changed)
self.sliders[name] = slider
self.layout.addWidget(slider, 1, i)
def set_sliders(self, vals):
# vals should a dict to of slider names and set vals
if len(vals) != len(self.sliders):
raise ValueError('Wrong number of values')
for key, value in vals.iteritems():
self.sliders[key].setValue(value)
class MixerPanel(QWidget):
'''A color mixer to hook up to an image.
You pass the image you the panel to operate on
and it operates on that image in place. You also
pass a callback to be called to trigger a refresh.
This callback is called every time the mixer modifies
your image.'''
def __init__(self, img, callback):
QWidget.__init__(self)
self.img = img
self.update = callback
self.mixer = ColorMixer(self.img)
#---------------------------------------------------------------
# ComboBox
#---------------------------------------------------------------
self.combo_box_entries = ['RGB Color', 'HSV Color',
'Brightness', 'Contrast']
self.combo_box = QtGui.QComboBox()
for entry in self.combo_box_entries:
self.combo_box.addItem(entry)
self.combo_box.currentIndexChanged.connect(self.combo_box_changed)
#---------------------------------------------------------------
# RGB color sliders
#---------------------------------------------------------------
# radio buttons
self.rgb_add = QtGui.QRadioButton('Additive')
self.rgb_mul = QtGui.QRadioButton('Multiplicative')
self.rgb_mul.toggled.connect(self.rgb_radio_changed)
self.rgb_add.toggled.connect(self.rgb_radio_changed)
# additive sliders
self.rgb_add_sliders = NSliderBlock(3, [(-255, 255, 0, 'R'),
(-255, 255, 0, 'G'),
(-255, 255, 0, 'B')],
self.rgb_add_changed)
# multiplicative sliders
self.rgb_mul_sliders = NSliderBlock(3, [(0, 1000, 500, 'R'),
(0, 1000, 500, 'G'),
(0, 1000, 500, 'B')],
self.rgb_mul_changed)
# layout
self.rgb_widget = QWidget()
self.rgb_widget.layout = QtGui.QGridLayout(self.rgb_widget)
self.rgb_widget.layout.addWidget(self.rgb_add, 0, 0)
self.rgb_widget.layout.addWidget(self.rgb_mul, 1, 0)
self.rgb_widget.layout.addWidget(self.rgb_add_sliders, 2, 0)
self.rgb_widget.layout.addWidget(self.rgb_mul_sliders, 2, 0)
#---------------------------------------------------------------
# Brightness sliders
#---------------------------------------------------------------
# sliders
self.bright_sliders = NSliderBlock(2, [(-255, 255, 0, 'OFF'),
(0, 1000, 500, 'FAC')],
self.bright_changed)
# layout
self.bright_widget = QWidget()
self.bright_widget.layout = QtGui.QGridLayout(self.bright_widget)
self.bright_widget.layout.addWidget(self.bright_sliders, 0, 0)
#---------------------------------------------------------------
# Buttons
#---------------------------------------------------------------
self.commit_button = QtGui.QPushButton('Commit')
self.commit_button.clicked.connect(self.commit_changes)
self.revert_button = QtGui.QPushButton('Revert')
self.revert_button.clicked.connect(self.revert_changes)
#---------------------------------------------------------------
# Mixer Layout
#---------------------------------------------------------------
self.layout = QtGui.QGridLayout(self)
self.layout.addWidget(self.combo_box, 0, 0)
self.layout.addWidget(self.rgb_widget, 1, 0)
self.layout.addWidget(self.bright_widget, 1, 0)
self.layout.addWidget(self.commit_button, 2, 0)
self.layout.addWidget(self.revert_button, 3, 0)
#---------------------------------------------------------------
# Initialization
#---------------------------------------------------------------
self.combo_box.setCurrentIndex(0)
self.hide_sliders()
self.rgb_widget.show()
self.rgb_add.setChecked(True)
def rgb_add_changed(self, name, val):
if not self.rgb_add.isChecked():
return
if name == 'R':
self.mixer.add(self.mixer.RED, val)
elif name == 'G':
self.mixer.add(self.mixer.GREEN, val)
elif name == 'B':
self.mixer.add(self.mixer.BLUE, val)
else:
return
self.update()
def rgb_mul_changed(self, name, val):
if not self.rgb_mul.isChecked():
return
val = val/500.
if name == 'R':
self.mixer.multiply(self.mixer.RED, val)
elif name == 'G':
self.mixer.multiply(self.mixer.GREEN, val)
elif name == 'B':
self.mixer.multiply(self.mixer.BLUE, val)
else:
return
self.update()
def bright_changed(self, name, val):
# doesnt matter which slider changed we need both
# values
factor = self.bright_sliders.sliders['FAC'].value() / 500.
offset = self.bright_sliders.sliders['OFF'].value()
self.mixer.brightness(offset, factor)
self.update()
def reset_sliders(self):
self.rgb_add_sliders.set_sliders({'R': 0, 'G': 0, 'B': 0})
self.rgb_mul_sliders.set_sliders({'R': 500, 'G': 500, 'B': 500})
self.bright_sliders.set_sliders({'OFF': 0, 'FAC': 500})
def combo_box_changed(self, index):
self.reset_sliders()
self.mixer.set_to_stateimg()
self.update()
combo_box_map={0: self.show_rgb, 1: self.show_hsv,
2: self.show_bright, 3: self.show_contrast}
combo_box_map[index]()
def hide_sliders(self):
self.rgb_widget.hide()
self.bright_widget.hide()
def rgb_radio_changed(self):
if self.rgb_add.isChecked():
self.rgb_add_sliders.show()
self.rgb_mul_sliders.hide()
elif self.rgb_mul.isChecked():
self.rgb_mul_sliders.show()
self.rgb_add_sliders.hide()
else:
pass
self.reset_sliders()
self.mixer.set_to_stateimg()
self.update()
def show_rgb(self):
self.hide_sliders()
self.rgb_widget.show()
def show_hsv(self):
self.hide_sliders()
def show_bright(self):
self.hide_sliders()
self.bright_widget.show()
def show_contrast(self):
self.hide_sliders()
def commit_changes(self):
self.mixer.commit_changes()
self.update()
def revert_changes(self):
self.mixer.revert()
self.reset_sliders()
self.update()
class FancyImageWindow(ImageWindow):
def __init__(self, arr, mgr):
ImageWindow.__init__(self, arr, mgr)
self.arr = arr
self.statusBar().showMessage('X: Y: ')
self.label.setScaledContents(True)
self.label.setMouseTracking(True)
self.mixer_panel = MixerPanel(self.arr, self.refresh_image)
self.layout.addWidget(self.mixer_panel)
self.mixer_panel.show()
def refresh_image(self):
pm = QPixmap.fromImage(self.label.img)
self.label.setPixmap(pm)
def scale_mouse_pos(self, x, y):
width = self.label.width()
height = self.label.height()
x_frac = 1. * x / width
y_frac = 1. * y / height
width = self.arr.shape[1]
height = self.arr.shape[0]
new_x = int(width * x_frac)
new_y = int(height * y_frac)
return(new_x, new_y)
def label_mouseMoveEvent(self, evt):
x = evt.x()
y = evt.y()
x, y = self.scale_mouse_pos(x, y)
msg = 'X: %d, Y: %d ' % (x, y)
R = self.arr[y,x,0]
G = self.arr[y,x,1]
B = self.arr[y,x,2]
msg += 'R: %s, G:, %s, B: %s' % (R, G, B)
self.statusBar().showMessage(msg)
def imshow(arr, fancy=False):
global app
if not app:
app = QApplication([])
arr = prepare_for_display(arr)
if not fancy:
iw = ImageWindow(arr, window_manager)
else:
iw = FancyImageWindow(arr, window_manager)
iw.show()
def _app_show():
global app
if app and window_manager.has_windows():
app.exec_()
else:
print 'No images to show. See `imshow`.'