Commit 2e37d9b1 authored by Luke Campagnola's avatar Luke Campagnola
Browse files

Merge tag 'pyqtgraph-0.9.10' into core

Conflicts:
	graphicsItems/ViewBox/ViewBox.py
	parametertree/SystemSolver.py
	widgets/SpinBox.py
parents ca3fbe2f 70cfdb4b
...@@ -84,8 +84,8 @@ class GraphicsScene(QtGui.QGraphicsScene): ...@@ -84,8 +84,8 @@ class GraphicsScene(QtGui.QGraphicsScene):
cls._addressCache[sip.unwrapinstance(sip.cast(obj, QtGui.QGraphicsItem))] = obj cls._addressCache[sip.unwrapinstance(sip.cast(obj, QtGui.QGraphicsItem))] = obj
def __init__(self, clickRadius=2, moveDistance=5): def __init__(self, clickRadius=2, moveDistance=5, parent=None):
QtGui.QGraphicsScene.__init__(self) QtGui.QGraphicsScene.__init__(self, parent)
self.setClickRadius(clickRadius) self.setClickRadius(clickRadius)
self.setMoveDistance(moveDistance) self.setMoveDistance(moveDistance)
self.exportDirectory = None self.exportDirectory = None
...@@ -135,8 +135,13 @@ class GraphicsScene(QtGui.QGraphicsScene): ...@@ -135,8 +135,13 @@ class GraphicsScene(QtGui.QGraphicsScene):
def mousePressEvent(self, ev): def mousePressEvent(self, ev):
#print 'scenePress' #print 'scenePress'
QtGui.QGraphicsScene.mousePressEvent(self, ev) QtGui.QGraphicsScene.mousePressEvent(self, ev)
#print "mouseGrabberItem: ", self.mouseGrabberItem()
if self.mouseGrabberItem() is None: ## nobody claimed press; we are free to generate drag/click events if self.mouseGrabberItem() is None: ## nobody claimed press; we are free to generate drag/click events
if self.lastHoverEvent is not None:
# If the mouse has moved since the last hover event, send a new one.
# This can happen if a context menu is open while the mouse is moving.
if ev.scenePos() != self.lastHoverEvent.scenePos():
self.sendHoverEvents(ev)
self.clickEvents.append(MouseClickEvent(ev)) self.clickEvents.append(MouseClickEvent(ev))
## set focus on the topmost focusable item under this click ## set focus on the topmost focusable item under this click
...@@ -145,10 +150,6 @@ class GraphicsScene(QtGui.QGraphicsScene): ...@@ -145,10 +150,6 @@ class GraphicsScene(QtGui.QGraphicsScene):
if i.isEnabled() and i.isVisible() and int(i.flags() & i.ItemIsFocusable) > 0: if i.isEnabled() and i.isVisible() and int(i.flags() & i.ItemIsFocusable) > 0:
i.setFocus(QtCore.Qt.MouseFocusReason) i.setFocus(QtCore.Qt.MouseFocusReason)
break break
#else:
#addr = sip.unwrapinstance(sip.cast(self.mouseGrabberItem(), QtGui.QGraphicsItem))
#item = GraphicsScene._addressCache.get(addr, self.mouseGrabberItem())
#print "click grabbed by:", item
def mouseMoveEvent(self, ev): def mouseMoveEvent(self, ev):
self.sigMouseMoved.emit(ev.scenePos()) self.sigMouseMoved.emit(ev.scenePos())
...@@ -189,7 +190,6 @@ class GraphicsScene(QtGui.QGraphicsScene): ...@@ -189,7 +190,6 @@ class GraphicsScene(QtGui.QGraphicsScene):
def mouseReleaseEvent(self, ev): def mouseReleaseEvent(self, ev):
#print 'sceneRelease' #print 'sceneRelease'
if self.mouseGrabberItem() is None: if self.mouseGrabberItem() is None:
#print "sending click/drag event"
if ev.button() in self.dragButtons: if ev.button() in self.dragButtons:
if self.sendDragEvent(ev, final=True): if self.sendDragEvent(ev, final=True):
#print "sent drag event" #print "sent drag event"
...@@ -231,6 +231,8 @@ class GraphicsScene(QtGui.QGraphicsScene): ...@@ -231,6 +231,8 @@ class GraphicsScene(QtGui.QGraphicsScene):
prevItems = list(self.hoverItems.keys()) prevItems = list(self.hoverItems.keys())
#print "hover prev items:", prevItems
#print "hover test items:", items
for item in items: for item in items:
if hasattr(item, 'hoverEvent'): if hasattr(item, 'hoverEvent'):
event.currentItem = item event.currentItem = item
...@@ -248,6 +250,7 @@ class GraphicsScene(QtGui.QGraphicsScene): ...@@ -248,6 +250,7 @@ class GraphicsScene(QtGui.QGraphicsScene):
event.enter = False event.enter = False
event.exit = True event.exit = True
#print "hover exit items:", prevItems
for item in prevItems: for item in prevItems:
event.currentItem = item event.currentItem = item
try: try:
...@@ -257,9 +260,13 @@ class GraphicsScene(QtGui.QGraphicsScene): ...@@ -257,9 +260,13 @@ class GraphicsScene(QtGui.QGraphicsScene):
finally: finally:
del self.hoverItems[item] del self.hoverItems[item]
if hasattr(ev, 'buttons') and int(ev.buttons()) == 0: # Update last hover event unless:
# - mouse is dragging (move+buttons); in this case we want the dragged
# item to continue receiving events until the drag is over
# - event is not a mouse event (QEvent.Leave sometimes appears here)
if (ev.type() == ev.GraphicsSceneMousePress or
(ev.type() == ev.GraphicsSceneMouseMove and int(ev.buttons()) == 0)):
self.lastHoverEvent = event ## save this so we can ask about accepted events later. self.lastHoverEvent = event ## save this so we can ask about accepted events later.
def sendDragEvent(self, ev, init=False, final=False): def sendDragEvent(self, ev, init=False, final=False):
## Send a MouseDragEvent to the current dragItem or to ## Send a MouseDragEvent to the current dragItem or to
...@@ -323,7 +330,6 @@ class GraphicsScene(QtGui.QGraphicsScene): ...@@ -323,7 +330,6 @@ class GraphicsScene(QtGui.QGraphicsScene):
acceptedItem = self.lastHoverEvent.clickItems().get(ev.button(), None) acceptedItem = self.lastHoverEvent.clickItems().get(ev.button(), None)
else: else:
acceptedItem = None acceptedItem = None
if acceptedItem is not None: if acceptedItem is not None:
ev.currentItem = acceptedItem ev.currentItem = acceptedItem
try: try:
...@@ -345,22 +351,9 @@ class GraphicsScene(QtGui.QGraphicsScene): ...@@ -345,22 +351,9 @@ class GraphicsScene(QtGui.QGraphicsScene):
if int(item.flags() & item.ItemIsFocusable) > 0: if int(item.flags() & item.ItemIsFocusable) > 0:
item.setFocus(QtCore.Qt.MouseFocusReason) item.setFocus(QtCore.Qt.MouseFocusReason)
break break
#if not ev.isAccepted() and ev.button() is QtCore.Qt.RightButton:
#print "GraphicsScene emitting sigSceneContextMenu"
#self.sigMouseClicked.emit(ev)
#ev.accept()
self.sigMouseClicked.emit(ev) self.sigMouseClicked.emit(ev)
return ev.isAccepted() return ev.isAccepted()
#def claimEvent(self, item, button, eventType):
#key = (button, eventType)
#if key in self.claimedEvents:
#return False
#self.claimedEvents[key] = item
#print "event", key, "claimed by", item
#return True
def items(self, *args): def items(self, *args):
#print 'args:', args #print 'args:', args
items = QtGui.QGraphicsScene.items(self, *args) items = QtGui.QGraphicsScene.items(self, *args)
......
...@@ -355,6 +355,9 @@ class HoverEvent(object): ...@@ -355,6 +355,9 @@ class HoverEvent(object):
return Point(self.currentItem.mapFromScene(self._lastScenePos)) return Point(self.currentItem.mapFromScene(self._lastScenePos))
def __repr__(self): def __repr__(self):
if self.exit:
return "<HoverEvent exit=True>"
if self.currentItem is None: if self.currentItem is None:
lp = self._lastScenePos lp = self._lastScenePos
p = self._scenePos p = self._scenePos
......
#
# The Python Imaging Library.
# $Id: Image.py 2933 2006-12-03 12:08:22Z fredrik $
#
# the Image class wrapper
#
# partial release history:
# 1995-09-09 fl Created
# 1996-03-11 fl PIL release 0.0 (proof of concept)
# 1996-04-30 fl PIL release 0.1b1
# 1999-07-28 fl PIL release 1.0 final
# 2000-06-07 fl PIL release 1.1
# 2000-10-20 fl PIL release 1.1.1
# 2001-05-07 fl PIL release 1.1.2
# 2002-03-15 fl PIL release 1.1.3
# 2003-05-10 fl PIL release 1.1.4
# 2005-03-28 fl PIL release 1.1.5
# 2006-12-02 fl PIL release 1.1.6
#
# Copyright (c) 1997-2006 by Secret Labs AB. All rights reserved.
# Copyright (c) 1995-2006 by Fredrik Lundh.
#
# See the README file for information on usage and redistribution.
#
VERSION = "1.1.6"
try:
import warnings
except ImportError:
warnings = None
class _imaging_not_installed:
# module placeholder
def __getattr__(self, id):
raise ImportError("The _imaging C module is not installed")
try:
# give Tk a chance to set up the environment, in case we're
# using an _imaging module linked against libtcl/libtk (use
# __import__ to hide this from naive packagers; we don't really
# depend on Tk unless ImageTk is used, and that module already
# imports Tkinter)
__import__("FixTk")
except ImportError:
pass
try:
# If the _imaging C module is not present, you can still use
# the "open" function to identify files, but you cannot load
# them. Note that other modules should not refer to _imaging
# directly; import Image and use the Image.core variable instead.
import _imaging
core = _imaging
del _imaging
except ImportError, v:
core = _imaging_not_installed()
if str(v)[:20] == "Module use of python" and warnings:
# The _imaging C module is present, but not compiled for
# the right version (windows only). Print a warning, if
# possible.
warnings.warn(
"The _imaging extension was built for another version "
"of Python; most PIL functions will be disabled",
RuntimeWarning
)
import ImageMode
import ImagePalette
import os, string, sys
# type stuff
from types import IntType, StringType, TupleType
try:
UnicodeStringType = type(unicode(""))
##
# (Internal) Checks if an object is a string. If the current
# Python version supports Unicode, this checks for both 8-bit
# and Unicode strings.
def isStringType(t):
return isinstance(t, StringType) or isinstance(t, UnicodeStringType)
except NameError:
def isStringType(t):
return isinstance(t, StringType)
##
# (Internal) Checks if an object is a tuple.
def isTupleType(t):
return isinstance(t, TupleType)
##
# (Internal) Checks if an object is an image object.
def isImageType(t):
return hasattr(t, "im")
##
# (Internal) Checks if an object is a string, and that it points to a
# directory.
def isDirectory(f):
return isStringType(f) and os.path.isdir(f)
from operator import isNumberType, isSequenceType
#
# Debug level
DEBUG = 0
#
# Constants (also defined in _imagingmodule.c!)
NONE = 0
# transpose
FLIP_LEFT_RIGHT = 0
FLIP_TOP_BOTTOM = 1
ROTATE_90 = 2
ROTATE_180 = 3
ROTATE_270 = 4
# transforms
AFFINE = 0
EXTENT = 1
PERSPECTIVE = 2
QUAD = 3
MESH = 4
# resampling filters
NONE = 0
NEAREST = 0
ANTIALIAS = 1 # 3-lobed lanczos
LINEAR = BILINEAR = 2
CUBIC = BICUBIC = 3
# dithers
NONE = 0
NEAREST = 0
ORDERED = 1 # Not yet implemented
RASTERIZE = 2 # Not yet implemented
FLOYDSTEINBERG = 3 # default
# palettes/quantizers
WEB = 0
ADAPTIVE = 1
# categories
NORMAL = 0
SEQUENCE = 1
CONTAINER = 2
# --------------------------------------------------------------------
# Registries
ID = []
OPEN = {}
MIME = {}
SAVE = {}
EXTENSION = {}
# --------------------------------------------------------------------
# Modes supported by this version
_MODEINFO = {
# NOTE: this table will be removed in future versions. use
# getmode* functions or ImageMode descriptors instead.
# official modes
"1": ("L", "L", ("1",)),
"L": ("L", "L", ("L",)),
"I": ("L", "I", ("I",)),
"F": ("L", "F", ("F",)),
"P": ("RGB", "L", ("P",)),
"RGB": ("RGB", "L", ("R", "G", "B")),
"RGBX": ("RGB", "L", ("R", "G", "B", "X")),
"RGBA": ("RGB", "L", ("R", "G", "B", "A")),
"CMYK": ("RGB", "L", ("C", "M", "Y", "K")),
"YCbCr": ("RGB", "L", ("Y", "Cb", "Cr")),
# Experimental modes include I;16, I;16B, RGBa, BGR;15,
# and BGR;24. Use these modes only if you know exactly
# what you're doing...
}
if sys.byteorder == 'little':
_ENDIAN = '<'
else:
_ENDIAN = '>'
_MODE_CONV = {
# official modes
"1": ('|b1', None),
"L": ('|u1', None),
"I": ('%si4' % _ENDIAN, None), # FIXME: is this correct?
"I;16": ('%su2' % _ENDIAN, None), # FIXME: is this correct?
"F": ('%sf4' % _ENDIAN, None), # FIXME: is this correct?
"P": ('|u1', None),
"RGB": ('|u1', 3),
"RGBX": ('|u1', 4),
"RGBA": ('|u1', 4),
"CMYK": ('|u1', 4),
"YCbCr": ('|u1', 4),
}
def _conv_type_shape(im):
shape = im.size[::-1]
typ, extra = _MODE_CONV[im.mode]
if extra is None:
return shape, typ
else:
return shape+(extra,), typ
MODES = _MODEINFO.keys()
MODES.sort()
# raw modes that may be memory mapped. NOTE: if you change this, you
# may have to modify the stride calculation in map.c too!
_MAPMODES = ("L", "P", "RGBX", "RGBA", "CMYK", "I;16", "I;16B")
##
# Gets the "base" mode for given mode. This function returns "L" for
# images that contain grayscale data, and "RGB" for images that
# contain color data.
#
# @param mode Input mode.
# @return "L" or "RGB".
# @exception KeyError If the input mode was not a standard mode.
def getmodebase(mode):
return ImageMode.getmode(mode).basemode
##
# Gets the storage type mode. Given a mode, this function returns a
# single-layer mode suitable for storing individual bands.
#
# @param mode Input mode.
# @return "L", "I", or "F".
# @exception KeyError If the input mode was not a standard mode.
def getmodetype(mode):
return ImageMode.getmode(mode).basetype
##
# Gets a list of individual band names. Given a mode, this function
# returns a tuple containing the names of individual bands (use
# {@link #getmodetype} to get the mode used to store each individual
# band.
#
# @param mode Input mode.
# @return A tuple containing band names. The length of the tuple
# gives the number of bands in an image of the given mode.
# @exception KeyError If the input mode was not a standard mode.
def getmodebandnames(mode):
return ImageMode.getmode(mode).bands
##
# Gets the number of individual bands for this mode.
#
# @param mode Input mode.
# @return The number of bands in this mode.
# @exception KeyError If the input mode was not a standard mode.
def getmodebands(mode):
return len(ImageMode.getmode(mode).bands)
# --------------------------------------------------------------------
# Helpers
_initialized = 0
##
# Explicitly loads standard file format drivers.
def preinit():
"Load standard file format drivers."
global _initialized
if _initialized >= 1:
return
try:
import BmpImagePlugin
except ImportError:
pass
try:
import GifImagePlugin
except ImportError:
pass
try:
import JpegImagePlugin
except ImportError:
pass
try:
import PpmImagePlugin
except ImportError:
pass
try:
import PngImagePlugin
except ImportError:
pass
# try:
# import TiffImagePlugin
# except ImportError:
# pass
_initialized = 1
##
# Explicitly initializes the Python Imaging Library. This function
# loads all available file format drivers.
def init():
"Load all file format drivers."
global _initialized
if _initialized >= 2:
return
visited = {}
directories = sys.path
try:
directories = directories + [os.path.dirname(__file__)]
except NameError:
pass
# only check directories (including current, if present in the path)
for directory in filter(isDirectory, directories):
fullpath = os.path.abspath(directory)
if visited.has_key(fullpath):
continue
for file in os.listdir(directory):
if file[-14:] == "ImagePlugin.py":
f, e = os.path.splitext(file)
try:
sys.path.insert(0, directory)
try:
__import__(f, globals(), locals(), [])
finally:
del sys.path[0]
except ImportError:
if DEBUG:
print "Image: failed to import",
print f, ":", sys.exc_value
visited[fullpath] = None
if OPEN or SAVE:
_initialized = 2
# --------------------------------------------------------------------
# Codec factories (used by tostring/fromstring and ImageFile.load)
def _getdecoder(mode, decoder_name, args, extra=()):
# tweak arguments
if args is None:
args = ()
elif not isTupleType(args):
args = (args,)
try:
# get decoder
decoder = getattr(core, decoder_name + "_decoder")
# print decoder, (mode,) + args + extra
return apply(decoder, (mode,) + args + extra)
except AttributeError:
raise IOError("decoder %s not available" % decoder_name)
def _getencoder(mode, encoder_name, args, extra=()):
# tweak arguments
if args is None:
args = ()
elif not isTupleType(args):
args = (args,)
try:
# get encoder
encoder = getattr(core, encoder_name + "_encoder")
# print encoder, (mode,) + args + extra
return apply(encoder, (mode,) + args + extra)
except AttributeError:
raise IOError("encoder %s not available" % encoder_name)
# --------------------------------------------------------------------
# Simple expression analyzer
class _E:
def __init__(self, data): self.data = data
def __coerce__(self, other): return self, _E(other)
def __add__(self, other): return _E((self.data, "__add__", other.data))
def __mul__(self, other): return _E((self.data, "__mul__", other.data))
def _getscaleoffset(expr):
stub = ["stub"]
data = expr(_E(stub)).data
try:
(a, b, c) = data # simplified syntax
if (a is stub and b == "__mul__" and isNumberType(c)):
return c, 0.0
if (a is stub and b == "__add__" and isNumberType(c)):
return 1.0, c
except TypeError: pass
try:
((a, b, c), d, e) = data # full syntax
if (a is stub and b == "__mul__" and isNumberType(c) and
d == "__add__" and isNumberType(e)):
return c, e
except TypeError: pass
raise ValueError("illegal expression")
# --------------------------------------------------------------------
# Implementation wrapper
##
# This class represents an image object. To create Image objects, use
# the appropriate factory functions. There's hardly ever any reason
# to call the Image constructor directly.
#
# @see #open
# @see #new
# @see #fromstring
class Image:
format = None
format_description = None
def __init__(self):
self.im = None
self.mode = ""
self.size = (0, 0)
self.palette = None
self.info = {}
self.category = NORMAL
self.readonly = 0
def _new(self, im):
new = Image()
new.im = im
new.mode = im.mode
new.size = im.size
new.palette = self.palette
if im.mode == "P":
new.palette = ImagePalette.ImagePalette()
try:
new.info = self.info.copy()
except AttributeError:
# fallback (pre-1.5.2)
new.info = {}
for k, v in self.info:
new.info[k] = v
return new
_makeself = _new # compatibility
def _copy(self):
self.load()
self.im = self.im.copy()
self.readonly = 0
def _dump(self, file=None, format=None):
import tempfile
if not file:
file = tempfile.mktemp()
self.load()