documentation updates
This commit is contained in:
parent
44f2a0ecc4
commit
4eadccdcc1
@ -23,36 +23,37 @@ class GraphicsScene(QtGui.QGraphicsScene):
|
|||||||
events, but this turned out to be impossible because the constructor for QGraphicsMouseEvent
|
events, but this turned out to be impossible because the constructor for QGraphicsMouseEvent
|
||||||
is private)
|
is private)
|
||||||
|
|
||||||
- Generates MouseClicked events in addition to the usual press/move/release events.
|
* Generates MouseClicked events in addition to the usual press/move/release events.
|
||||||
(This works around a problem where it is impossible to have one item respond to a
|
(This works around a problem where it is impossible to have one item respond to a
|
||||||
drag if another is watching for a click.)
|
drag if another is watching for a click.)
|
||||||
- Adjustable radius around click that will catch objects so you don't have to click *exactly* over small/thin objects
|
* Adjustable radius around click that will catch objects so you don't have to click *exactly* over small/thin objects
|
||||||
- Global context menu--if an item implements a context menu, then its parent(s) may also add items to the menu.
|
* Global context menu--if an item implements a context menu, then its parent(s) may also add items to the menu.
|
||||||
- Allows items to decide _before_ a mouse click which item will be the recipient of mouse events.
|
* Allows items to decide _before_ a mouse click which item will be the recipient of mouse events.
|
||||||
This lets us indicate unambiguously to the user which item they are about to click/drag on
|
This lets us indicate unambiguously to the user which item they are about to click/drag on
|
||||||
- Eats mouseMove events that occur too soon after a mouse press.
|
* Eats mouseMove events that occur too soon after a mouse press.
|
||||||
- Reimplements items() and itemAt() to circumvent PyQt bug
|
* Reimplements items() and itemAt() to circumvent PyQt bug
|
||||||
|
|
||||||
Mouse interaction is as follows:
|
Mouse interaction is as follows:
|
||||||
|
|
||||||
1) Every time the mouse moves, the scene delivers both the standard hoverEnter/Move/LeaveEvents
|
1) Every time the mouse moves, the scene delivers both the standard hoverEnter/Move/LeaveEvents
|
||||||
as well as custom HoverEvents.
|
as well as custom HoverEvents.
|
||||||
2) Items are sent HoverEvents in Z-order and each item may optionally call event.acceptClicks(button),
|
2) Items are sent HoverEvents in Z-order and each item may optionally call event.acceptClicks(button),
|
||||||
acceptDrags(button) or both. If this method call returns True, this informs the item that _if_
|
acceptDrags(button) or both. If this method call returns True, this informs the item that _if_
|
||||||
the user clicks/drags the specified mouse button, the item is guaranteed to be the
|
the user clicks/drags the specified mouse button, the item is guaranteed to be the
|
||||||
recipient of click/drag events (the item may wish to change its appearance to indicate this).
|
recipient of click/drag events (the item may wish to change its appearance to indicate this).
|
||||||
If the call to acceptClicks/Drags returns False, then the item is guaranteed to NOT receive
|
If the call to acceptClicks/Drags returns False, then the item is guaranteed to *not* receive
|
||||||
the requested event (because another item has already accepted it).
|
the requested event (because another item has already accepted it).
|
||||||
3) If the mouse is clicked, a mousePressEvent is generated as usual. If any items accept this press event, then
|
3) If the mouse is clicked, a mousePressEvent is generated as usual. If any items accept this press event, then
|
||||||
No click/drag events will be generated and mouse interaction proceeds as defined by Qt. This allows
|
No click/drag events will be generated and mouse interaction proceeds as defined by Qt. This allows
|
||||||
items to function properly if they are expecting the usual press/move/release sequence of events.
|
items to function properly if they are expecting the usual press/move/release sequence of events.
|
||||||
(It is recommended that items do NOT accept press events, and instead use click/drag events)
|
(It is recommended that items do NOT accept press events, and instead use click/drag events)
|
||||||
Note: The default implementation of QGraphicsItem.mousePressEvent will ACCEPT the event if the
|
Note: The default implementation of QGraphicsItem.mousePressEvent will *accept* the event if the
|
||||||
item is has its Selectable or Movable flags enabled. You may need to override this behavior.
|
item is has its Selectable or Movable flags enabled. You may need to override this behavior.
|
||||||
3) If no item accepts the mousePressEvent, then the scene will begin delivering mouseDrag and/or mouseClick events.
|
4) If no item accepts the mousePressEvent, then the scene will begin delivering mouseDrag and/or mouseClick events.
|
||||||
If the mouse is moved a sufficient distance (or moved slowly enough) before the button is released,
|
If the mouse is moved a sufficient distance (or moved slowly enough) before the button is released,
|
||||||
then a mouseDragEvent is generated.
|
then a mouseDragEvent is generated.
|
||||||
If no drag events are generated before the button is released, then a mouseClickEvent is generated.
|
If no drag events are generated before the button is released, then a mouseClickEvent is generated.
|
||||||
4) Click/drag events are delivered to the item that called acceptClicks/acceptDrags on the HoverEvent
|
5) Click/drag events are delivered to the item that called acceptClicks/acceptDrags on the HoverEvent
|
||||||
in step 1. If no such items exist, then the scene attempts to deliver the events to items near the event.
|
in step 1. If no such items exist, then the scene attempts to deliver the events to items near the event.
|
||||||
ClickEvents may be delivered in this way even if no
|
ClickEvents may be delivered in this way even if no
|
||||||
item originally claimed it could accept the click. DragEvents may only be delivered this way if it is the initial
|
item originally claimed it could accept the click. DragEvents may only be delivered this way if it is the initial
|
||||||
@ -470,23 +471,25 @@ class GraphicsScene(QtGui.QGraphicsScene):
|
|||||||
|
|
||||||
The final menu will look like:
|
The final menu will look like:
|
||||||
|
|
||||||
Original Item 1
|
| Original Item 1
|
||||||
Original Item 2
|
| Original Item 2
|
||||||
...
|
| ...
|
||||||
Original Item N
|
| Original Item N
|
||||||
------------------
|
| ------------------
|
||||||
Parent Item 1
|
| Parent Item 1
|
||||||
Parent Item 2
|
| Parent Item 2
|
||||||
...
|
| ...
|
||||||
Grandparent Item 1
|
| Grandparent Item 1
|
||||||
...
|
| ...
|
||||||
|
|
||||||
|
|
||||||
Arguments:
|
============== ==================================================
|
||||||
item - The item that initially created the context menu
|
**Arguments:**
|
||||||
(This is probably the item making the call to this function)
|
item The item that initially created the context menu
|
||||||
menu - The context menu being shown by the item
|
(This is probably the item making the call to this function)
|
||||||
event - The original event that triggered the menu to appear.
|
menu The context menu being shown by the item
|
||||||
|
event The original event that triggered the menu to appear.
|
||||||
|
============== ==================================================
|
||||||
"""
|
"""
|
||||||
|
|
||||||
#items = self.itemsNearEvent(ev)
|
#items = self.itemsNearEvent(ev)
|
||||||
|
@ -4,6 +4,13 @@ import weakref
|
|||||||
import pyqtgraph.ptime as ptime
|
import pyqtgraph.ptime as ptime
|
||||||
|
|
||||||
class MouseDragEvent:
|
class MouseDragEvent:
|
||||||
|
"""
|
||||||
|
Instances of this class are delivered to items in a :class:`GraphicsScene <pyqtgraph.GraphicsScene>` via their mouseDragEvent() method when the item is being mouse-dragged.
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def __init__(self, moveEvent, pressEvent, lastEvent, start=False, finish=False):
|
def __init__(self, moveEvent, pressEvent, lastEvent, start=False, finish=False):
|
||||||
self.start = start
|
self.start = start
|
||||||
self.finish = finish
|
self.finish = finish
|
||||||
@ -27,59 +34,99 @@ class MouseDragEvent:
|
|||||||
self._modifiers = moveEvent.modifiers()
|
self._modifiers = moveEvent.modifiers()
|
||||||
|
|
||||||
def accept(self):
|
def accept(self):
|
||||||
|
"""An item should call this method if it can handle the event. This will prevent the event being delivered to any other items."""
|
||||||
self.accepted = True
|
self.accepted = True
|
||||||
self.acceptedItem = self.currentItem
|
self.acceptedItem = self.currentItem
|
||||||
|
|
||||||
def ignore(self):
|
def ignore(self):
|
||||||
|
"""An item should call this method if it cannot handle the event. This will allow the event to be delivered to other items."""
|
||||||
self.accepted = False
|
self.accepted = False
|
||||||
|
|
||||||
def isAccepted(self):
|
def isAccepted(self):
|
||||||
return self.accepted
|
return self.accepted
|
||||||
|
|
||||||
def scenePos(self):
|
def scenePos(self):
|
||||||
|
"""Return the current scene position of the mouse."""
|
||||||
return Point(self._scenePos)
|
return Point(self._scenePos)
|
||||||
|
|
||||||
def screenPos(self):
|
def screenPos(self):
|
||||||
|
"""Return the current screen position (pixels relative to widget) of the mouse."""
|
||||||
return Point(self._screenPos)
|
return Point(self._screenPos)
|
||||||
|
|
||||||
def buttonDownScenePos(self, btn=None):
|
def buttonDownScenePos(self, btn=None):
|
||||||
|
"""
|
||||||
|
Return the scene position of the mouse at the time *btn* was pressed.
|
||||||
|
If *btn* is omitted, then the button that initiated the drag is assumed.
|
||||||
|
"""
|
||||||
if btn is None:
|
if btn is None:
|
||||||
btn = self.button()
|
btn = self.button()
|
||||||
return Point(self._buttonDownScenePos[int(btn)])
|
return Point(self._buttonDownScenePos[int(btn)])
|
||||||
|
|
||||||
def buttonDownScreenPos(self, btn=None):
|
def buttonDownScreenPos(self, btn=None):
|
||||||
|
"""
|
||||||
|
Return the screen position (pixels relative to widget) of the mouse at the time *btn* was pressed.
|
||||||
|
If *btn* is omitted, then the button that initiated the drag is assumed.
|
||||||
|
"""
|
||||||
if btn is None:
|
if btn is None:
|
||||||
btn = self.button()
|
btn = self.button()
|
||||||
return Point(self._buttonDownScreenPos[int(btn)])
|
return Point(self._buttonDownScreenPos[int(btn)])
|
||||||
|
|
||||||
def lastScenePos(self):
|
def lastScenePos(self):
|
||||||
|
"""
|
||||||
|
Return the scene position of the mouse immediately prior to this event.
|
||||||
|
"""
|
||||||
return Point(self._lastScenePos)
|
return Point(self._lastScenePos)
|
||||||
|
|
||||||
def lastScreenPos(self):
|
def lastScreenPos(self):
|
||||||
|
"""
|
||||||
|
Return the screen position of the mouse immediately prior to this event.
|
||||||
|
"""
|
||||||
return Point(self._lastScreenPos)
|
return Point(self._lastScreenPos)
|
||||||
|
|
||||||
def buttons(self):
|
def buttons(self):
|
||||||
|
"""
|
||||||
|
Return the buttons currently pressed on the mouse.
|
||||||
|
(see QGraphicsSceneMouseEvent::buttons in the Qt documentation)
|
||||||
|
"""
|
||||||
return self._buttons
|
return self._buttons
|
||||||
|
|
||||||
def button(self):
|
def button(self):
|
||||||
"""Return the button that initiated the drag (may be different from the buttons currently pressed)"""
|
"""Return the button that initiated the drag (may be different from the buttons currently pressed)
|
||||||
|
(see QGraphicsSceneMouseEvent::button in the Qt documentation)
|
||||||
|
|
||||||
|
"""
|
||||||
return self._button
|
return self._button
|
||||||
|
|
||||||
def pos(self):
|
def pos(self):
|
||||||
|
"""
|
||||||
|
Return the current position of the mouse in the coordinate system of the item
|
||||||
|
that the event was delivered to.
|
||||||
|
"""
|
||||||
return Point(self.currentItem.mapFromScene(self._scenePos))
|
return Point(self.currentItem.mapFromScene(self._scenePos))
|
||||||
|
|
||||||
def lastPos(self):
|
def lastPos(self):
|
||||||
|
"""
|
||||||
|
Return the previous position of the mouse in the coordinate system of the item
|
||||||
|
that the event was delivered to.
|
||||||
|
"""
|
||||||
return Point(self.currentItem.mapFromScene(self._lastScenePos))
|
return Point(self.currentItem.mapFromScene(self._lastScenePos))
|
||||||
|
|
||||||
def buttonDownPos(self, btn=None):
|
def buttonDownPos(self, btn=None):
|
||||||
|
"""
|
||||||
|
Return the position of the mouse at the time the drag was initiated
|
||||||
|
in the coordinate system of the item that the event was delivered to.
|
||||||
|
"""
|
||||||
if btn is None:
|
if btn is None:
|
||||||
btn = self.button()
|
btn = self.button()
|
||||||
return Point(self.currentItem.mapFromScene(self._buttonDownScenePos[int(btn)]))
|
return Point(self.currentItem.mapFromScene(self._buttonDownScenePos[int(btn)]))
|
||||||
|
|
||||||
def isStart(self):
|
def isStart(self):
|
||||||
|
"""Returns True if this event is the first since a drag was initiated."""
|
||||||
return self.start
|
return self.start
|
||||||
|
|
||||||
def isFinish(self):
|
def isFinish(self):
|
||||||
|
"""Returns False if this is the last event in a drag. Note that this
|
||||||
|
event will have the same position as the previous one."""
|
||||||
return self.finish
|
return self.finish
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
@ -88,11 +135,21 @@ class MouseDragEvent:
|
|||||||
return "<MouseDragEvent (%g,%g)->(%g,%g) buttons=%d start=%s finish=%s>" % (lp.x(), lp.y(), p.x(), p.y(), int(self.buttons()), str(self.isStart()), str(self.isFinish()))
|
return "<MouseDragEvent (%g,%g)->(%g,%g) buttons=%d start=%s finish=%s>" % (lp.x(), lp.y(), p.x(), p.y(), int(self.buttons()), str(self.isStart()), str(self.isFinish()))
|
||||||
|
|
||||||
def modifiers(self):
|
def modifiers(self):
|
||||||
|
"""Return any keyboard modifiers currently pressed.
|
||||||
|
(see QGraphicsSceneMouseEvent::modifiers in the Qt documentation)
|
||||||
|
|
||||||
|
"""
|
||||||
return self._modifiers
|
return self._modifiers
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class MouseClickEvent:
|
class MouseClickEvent:
|
||||||
|
"""
|
||||||
|
Instances of this class are delivered to items in a :class:`GraphicsScene <pyqtgraph.GraphicsScene>` via their mouseClickEvent() method when the item is clicked.
|
||||||
|
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
def __init__(self, pressEvent, double=False):
|
def __init__(self, pressEvent, double=False):
|
||||||
self.accepted = False
|
self.accepted = False
|
||||||
self.currentItem = None
|
self.currentItem = None
|
||||||
@ -106,37 +163,60 @@ class MouseClickEvent:
|
|||||||
|
|
||||||
|
|
||||||
def accept(self):
|
def accept(self):
|
||||||
|
"""An item should call this method if it can handle the event. This will prevent the event being delivered to any other items."""
|
||||||
self.accepted = True
|
self.accepted = True
|
||||||
self.acceptedItem = self.currentItem
|
self.acceptedItem = self.currentItem
|
||||||
|
|
||||||
def ignore(self):
|
def ignore(self):
|
||||||
|
"""An item should call this method if it cannot handle the event. This will allow the event to be delivered to other items."""
|
||||||
self.accepted = False
|
self.accepted = False
|
||||||
|
|
||||||
def isAccepted(self):
|
def isAccepted(self):
|
||||||
return self.accepted
|
return self.accepted
|
||||||
|
|
||||||
def scenePos(self):
|
def scenePos(self):
|
||||||
|
"""Return the current scene position of the mouse."""
|
||||||
return Point(self._scenePos)
|
return Point(self._scenePos)
|
||||||
|
|
||||||
def screenPos(self):
|
def screenPos(self):
|
||||||
|
"""Return the current screen position (pixels relative to widget) of the mouse."""
|
||||||
return Point(self._screenPos)
|
return Point(self._screenPos)
|
||||||
|
|
||||||
def buttons(self):
|
def buttons(self):
|
||||||
|
"""
|
||||||
|
Return the buttons currently pressed on the mouse.
|
||||||
|
(see QGraphicsSceneMouseEvent::buttons in the Qt documentation)
|
||||||
|
"""
|
||||||
return self._buttons
|
return self._buttons
|
||||||
|
|
||||||
def button(self):
|
def button(self):
|
||||||
|
"""Return the mouse button that generated the click event.
|
||||||
|
(see QGraphicsSceneMouseEvent::button in the Qt documentation)
|
||||||
|
"""
|
||||||
return self._button
|
return self._button
|
||||||
|
|
||||||
def double(self):
|
def double(self):
|
||||||
|
"""Return True if this is a double-click."""
|
||||||
return self._double
|
return self._double
|
||||||
|
|
||||||
def pos(self):
|
def pos(self):
|
||||||
|
"""
|
||||||
|
Return the current position of the mouse in the coordinate system of the item
|
||||||
|
that the event was delivered to.
|
||||||
|
"""
|
||||||
return Point(self.currentItem.mapFromScene(self._scenePos))
|
return Point(self.currentItem.mapFromScene(self._scenePos))
|
||||||
|
|
||||||
def lastPos(self):
|
def lastPos(self):
|
||||||
|
"""
|
||||||
|
Return the previous position of the mouse in the coordinate system of the item
|
||||||
|
that the event was delivered to.
|
||||||
|
"""
|
||||||
return Point(self.currentItem.mapFromScene(self._lastScenePos))
|
return Point(self.currentItem.mapFromScene(self._lastScenePos))
|
||||||
|
|
||||||
def modifiers(self):
|
def modifiers(self):
|
||||||
|
"""Return any keyboard modifiers currently pressed.
|
||||||
|
(see QGraphicsSceneMouseEvent::modifiers in the Qt documentation)
|
||||||
|
"""
|
||||||
return self._modifiers
|
return self._modifiers
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
@ -150,8 +230,9 @@ class MouseClickEvent:
|
|||||||
|
|
||||||
class HoverEvent:
|
class HoverEvent:
|
||||||
"""
|
"""
|
||||||
|
Instances of this class are delivered to items in a :class:`GraphicsScene <pyqtgraph.GraphicsScene>` via their hoverEvent() method when the mouse is hovering over the item.
|
||||||
This event class both informs items that the mouse cursor is nearby and allows items to
|
This event class both informs items that the mouse cursor is nearby and allows items to
|
||||||
communicate with one another about whether each item will accept _potential_ mouse events.
|
communicate with one another about whether each item will accept *potential* mouse events.
|
||||||
|
|
||||||
It is common for multiple overlapping items to receive hover events and respond by changing
|
It is common for multiple overlapping items to receive hover events and respond by changing
|
||||||
their appearance. This can be misleading to the user since, in general, only one item will
|
their appearance. This can be misleading to the user since, in general, only one item will
|
||||||
@ -188,13 +269,21 @@ class HoverEvent:
|
|||||||
|
|
||||||
|
|
||||||
def isEnter(self):
|
def isEnter(self):
|
||||||
|
"""Returns True if the mouse has just entered the item's shape"""
|
||||||
return self.enter
|
return self.enter
|
||||||
|
|
||||||
def isExit(self):
|
def isExit(self):
|
||||||
|
"""Returns True if the mouse has just exited the item's shape"""
|
||||||
return self.exit
|
return self.exit
|
||||||
|
|
||||||
def acceptClicks(self, button):
|
def acceptClicks(self, button):
|
||||||
""""""
|
"""Inform the scene that the item (that the event was delivered to)
|
||||||
|
would accept a mouse click event if the user were to click before
|
||||||
|
moving the mouse again.
|
||||||
|
|
||||||
|
Returns True if the request is successful, otherwise returns False (indicating
|
||||||
|
that some other item would receive an incoming click).
|
||||||
|
"""
|
||||||
if not self.acceptable:
|
if not self.acceptable:
|
||||||
return False
|
return False
|
||||||
if button not in self.__clickItems:
|
if button not in self.__clickItems:
|
||||||
@ -203,6 +292,13 @@ class HoverEvent:
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
def acceptDrags(self, button):
|
def acceptDrags(self, button):
|
||||||
|
"""Inform the scene that the item (that the event was delivered to)
|
||||||
|
would accept a mouse drag event if the user were to drag before
|
||||||
|
the next hover event.
|
||||||
|
|
||||||
|
Returns True if the request is successful, otherwise returns False (indicating
|
||||||
|
that some other item would receive an incoming drag event).
|
||||||
|
"""
|
||||||
if not self.acceptable:
|
if not self.acceptable:
|
||||||
return False
|
return False
|
||||||
if button not in self.__dragItems:
|
if button not in self.__dragItems:
|
||||||
@ -211,24 +307,40 @@ class HoverEvent:
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
def scenePos(self):
|
def scenePos(self):
|
||||||
|
"""Return the current scene position of the mouse."""
|
||||||
return Point(self._scenePos)
|
return Point(self._scenePos)
|
||||||
|
|
||||||
def screenPos(self):
|
def screenPos(self):
|
||||||
|
"""Return the current screen position of the mouse."""
|
||||||
return Point(self._screenPos)
|
return Point(self._screenPos)
|
||||||
|
|
||||||
def lastScenePos(self):
|
def lastScenePos(self):
|
||||||
|
"""Return the previous scene position of the mouse."""
|
||||||
return Point(self._lastScenePos)
|
return Point(self._lastScenePos)
|
||||||
|
|
||||||
def lastScreenPos(self):
|
def lastScreenPos(self):
|
||||||
|
"""Return the previous screen position of the mouse."""
|
||||||
return Point(self._lastScreenPos)
|
return Point(self._lastScreenPos)
|
||||||
|
|
||||||
def buttons(self):
|
def buttons(self):
|
||||||
|
"""
|
||||||
|
Return the buttons currently pressed on the mouse.
|
||||||
|
(see QGraphicsSceneMouseEvent::buttons in the Qt documentation)
|
||||||
|
"""
|
||||||
return self._buttons
|
return self._buttons
|
||||||
|
|
||||||
def pos(self):
|
def pos(self):
|
||||||
|
"""
|
||||||
|
Return the current position of the mouse in the coordinate system of the item
|
||||||
|
that the event was delivered to.
|
||||||
|
"""
|
||||||
return Point(self.currentItem.mapFromScene(self._scenePos))
|
return Point(self.currentItem.mapFromScene(self._scenePos))
|
||||||
|
|
||||||
def lastPos(self):
|
def lastPos(self):
|
||||||
|
"""
|
||||||
|
Return the previous position of the mouse in the coordinate system of the item
|
||||||
|
that the event was delivered to.
|
||||||
|
"""
|
||||||
return Point(self.currentItem.mapFromScene(self._lastScenePos))
|
return Point(self.currentItem.mapFromScene(self._lastScenePos))
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
@ -237,6 +349,9 @@ class HoverEvent:
|
|||||||
return "<HoverEvent (%g,%g)->(%g,%g) buttons=%d enter=%s exit=%s>" % (lp.x(), lp.y(), p.x(), p.y(), int(self.buttons()), str(self.isEnter()), str(self.isExit()))
|
return "<HoverEvent (%g,%g)->(%g,%g) buttons=%d enter=%s exit=%s>" % (lp.x(), lp.y(), p.x(), p.y(), int(self.buttons()), str(self.isEnter()), str(self.isExit()))
|
||||||
|
|
||||||
def modifiers(self):
|
def modifiers(self):
|
||||||
|
"""Return any keyboard modifiers currently pressed.
|
||||||
|
(see QGraphicsSceneMouseEvent::modifiers in the Qt documentation)
|
||||||
|
"""
|
||||||
return self._modifiers
|
return self._modifiers
|
||||||
|
|
||||||
def clickItems(self):
|
def clickItems(self):
|
||||||
|
16
__init__.py
16
__init__.py
@ -119,9 +119,10 @@ QAPP = None
|
|||||||
|
|
||||||
def plot(*args, **kargs):
|
def plot(*args, **kargs):
|
||||||
"""
|
"""
|
||||||
| Create and return a PlotWindow (this is just a window with PlotWidget inside), plot data in it.
|
Create and return a :class:`PlotWindow <pyqtgraph.PlotWindow>`
|
||||||
| Accepts a *title* argument to set the title of the window.
|
(this is just a window with :class:`PlotWidget <pyqtgraph.PlotWidget>` inside), plot data in it.
|
||||||
| All other arguments are used to plot data. (see :func:`PlotItem.plot() <pyqtgraph.PlotItem.plot>`)
|
Accepts a *title* argument to set the title of the window.
|
||||||
|
All other arguments are used to plot data. (see :func:`PlotItem.plot() <pyqtgraph.PlotItem.plot>`)
|
||||||
"""
|
"""
|
||||||
mkQApp()
|
mkQApp()
|
||||||
#if 'title' in kargs:
|
#if 'title' in kargs:
|
||||||
@ -149,10 +150,11 @@ def plot(*args, **kargs):
|
|||||||
|
|
||||||
def image(*args, **kargs):
|
def image(*args, **kargs):
|
||||||
"""
|
"""
|
||||||
| Create and return an ImageWindow (this is just a window with ImageView widget inside), show image data inside.
|
Create and return an :class:`ImageWindow <pyqtgraph.ImageWindow>`
|
||||||
| Will show 2D or 3D image data.
|
(this is just a window with :class:`ImageView <pyqtgraph.ImageView>` widget inside), show image data inside.
|
||||||
| Accepts a *title* argument to set the title of the window.
|
Will show 2D or 3D image data.
|
||||||
| All other arguments are used to show data. (see :func:`ImageView.setImage() <pyqtgraph.ImageView.setImage>`)
|
Accepts a *title* argument to set the title of the window.
|
||||||
|
All other arguments are used to show data. (see :func:`ImageView.setImage() <pyqtgraph.ImageView.setImage>`)
|
||||||
"""
|
"""
|
||||||
mkQApp()
|
mkQApp()
|
||||||
w = ImageWindow(*args, **kargs)
|
w = ImageWindow(*args, **kargs)
|
||||||
|
@ -9,3 +9,4 @@ Contents:
|
|||||||
functions
|
functions
|
||||||
graphicsItems/index
|
graphicsItems/index
|
||||||
widgets/index
|
widgets/index
|
||||||
|
graphicsscene/index
|
8
documentation/source/graphicsscene/graphicsscene.rst
Normal file
8
documentation/source/graphicsscene/graphicsscene.rst
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
GraphicsScene
|
||||||
|
=============
|
||||||
|
|
||||||
|
.. autoclass:: pyqtgraph.GraphicsScene
|
||||||
|
:members:
|
||||||
|
|
||||||
|
.. automethod:: pyqtgraph.GraphicsScene.__init__
|
||||||
|
|
5
documentation/source/graphicsscene/hoverevent.rst
Normal file
5
documentation/source/graphicsscene/hoverevent.rst
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
HoverEvent
|
||||||
|
==========
|
||||||
|
|
||||||
|
.. autoclass:: pyqtgraph.GraphicsScene.mouseEvents.HoverEvent
|
||||||
|
:members:
|
12
documentation/source/graphicsscene/index.rst
Normal file
12
documentation/source/graphicsscene/index.rst
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
GraphicsScene and Mouse Events
|
||||||
|
==============================
|
||||||
|
|
||||||
|
Contents:
|
||||||
|
|
||||||
|
.. toctree::
|
||||||
|
:maxdepth: 2
|
||||||
|
|
||||||
|
graphicsscene
|
||||||
|
hoverevent
|
||||||
|
mouseclickevent
|
||||||
|
mousedragevent
|
5
documentation/source/graphicsscene/mouseclickevent.rst
Normal file
5
documentation/source/graphicsscene/mouseclickevent.rst
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
MouseClickEvent
|
||||||
|
===============
|
||||||
|
|
||||||
|
.. autoclass:: pyqtgraph.GraphicsScene.mouseEvents.MouseClickEvent
|
||||||
|
:members:
|
5
documentation/source/graphicsscene/mousedragevent.rst
Normal file
5
documentation/source/graphicsscene/mousedragevent.rst
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
MouseDragEvent
|
||||||
|
==============
|
||||||
|
|
||||||
|
.. autoclass:: pyqtgraph.GraphicsScene.mouseEvents.MouseDragEvent
|
||||||
|
:members:
|
@ -17,7 +17,7 @@ Pyqtgraph makes it very easy to visualize data from the command line. Observe::
|
|||||||
import pyqtgraph as pg
|
import pyqtgraph as pg
|
||||||
pg.plot(data) # data can be a list of values or a numpy array
|
pg.plot(data) # data can be a list of values or a numpy array
|
||||||
|
|
||||||
The example above would open a window displaying a line plot of the data given. I don't think it could reasonably be any simpler than that. The call to pg.plot returns a handle to the plot widget that is created, allowing more data to be added to the same window.
|
The example above would open a window displaying a line plot of the data given. The call to :func:`pg.plot <pyqtgraph.plot>` returns a handle to the :class:`plot widget <pyqtgraph.PlotWidget>` that is created, allowing more data to be added to the same window.
|
||||||
|
|
||||||
Further examples::
|
Further examples::
|
||||||
|
|
||||||
@ -43,5 +43,5 @@ While I consider this approach somewhat lazy, it is often the case that 'lazy' i
|
|||||||
Embedding widgets inside PyQt applications
|
Embedding widgets inside PyQt applications
|
||||||
------------------------------------------
|
------------------------------------------
|
||||||
|
|
||||||
For the serious application developer, all of the functionality in pyqtgraph is available via widgets that can be embedded just like any other Qt widgets. Most importantly, see: PlotWidget, ImageView, GraphicsView, GraphicsLayoutWidget. Pyqtgraph's widgets can be included in Designer's ui files via the "Promote To..." functionality.
|
For the serious application developer, all of the functionality in pyqtgraph is available via widgets that can be embedded just like any other Qt widgets. Most importantly, see: :class:`PlotWidget <pyqtgraph.PlotWidget>`, :class:`ImageView <pyqtgraph.ImageView>`, :class:`GraphicsLayoutWidget <pyqtgraph.GraphicsLayoutWidget>`, and :class:`GraphicsView <pyqtgraph.GraphicsView>`. Pyqtgraph's widgets can be included in Designer's ui files via the "Promote To..." functionality.
|
||||||
|
|
||||||
|
@ -44,8 +44,8 @@ This will start a launcher with a list of available examples. Select an item fro
|
|||||||
How does it compare to...
|
How does it compare to...
|
||||||
-------------------------
|
-------------------------
|
||||||
|
|
||||||
* matplotlib: For plotting and making publication-quality graphics, matplotlib is far more mature than pyqtgraph. However, matplotlib is also much slower and not suitable for applications requiring realtime update of plots/video or rapid interactivity. It also does not provide any of the GUI tools and image interaction/slicing functionality in pyqtgraph.
|
* matplotlib: For plotting, pyqtgraph is not nearly as complete/mature as matplotlib, but runs much faster. Matplotlib is more aimed toward making publication-quality graphics, whereas pyqtgraph is intended for use in data acquisition and analysis applications. Matplotlib is more intuitive for matlab programmers; pyqtgraph is more intuitive for python/qt programmers. Matplotlib (to my knowledge) does not include many of pyqtgraph's features such as image interaction, volumetric rendering, parameter trees, flowcharts, etc.
|
||||||
|
|
||||||
* pyqwt5: pyqwt is generally more mature than pyqtgraph for plotting and is about as fast. The major differences are 1) pyqtgraph is written in pure python, so it is somewhat more portable than pyqwt, which often lags behind pyqt in development (and can be a pain to install on some platforms) and 2) like matplotlib, pyqwt does not provide any of the GUI tools and image interaction/slicing functionality in pyqtgraph.
|
* pyqwt5: About as fast as pyqwt5, but not quite as complete for plotting functionality. Image handling in pyqtgraph is much more complete (again, no ROI widgets in qwt). Also, pyqtgraph is written in pure python, so it is more portable than pyqwt, which often lags behind pyqt in development (I originally used pyqwt, but decided it was too much trouble to rely on it as a dependency in my projects). Like matplotlib, pyqwt (to my knowledge) does not include many of pyqtgraph's features such as image interaction, volumetric rendering, parameter trees, flowcharts, etc.
|
||||||
|
|
||||||
(My experience with these libraries is somewhat outdated; please correct me if I am wrong here)
|
(My experience with these libraries is somewhat outdated; please correct me if I am wrong here)
|
||||||
|
@ -780,6 +780,9 @@ class PlotItem(GraphicsWidget):
|
|||||||
|
|
||||||
|
|
||||||
def removeItem(self, item):
|
def removeItem(self, item):
|
||||||
|
"""
|
||||||
|
Remove an item from the internal ViewBox.
|
||||||
|
"""
|
||||||
if not item in self.items:
|
if not item in self.items:
|
||||||
return
|
return
|
||||||
self.items.remove(item)
|
self.items.remove(item)
|
||||||
@ -796,6 +799,9 @@ class PlotItem(GraphicsWidget):
|
|||||||
#item.sigPlotChanged.connect(self.plotChanged)
|
#item.sigPlotChanged.connect(self.plotChanged)
|
||||||
|
|
||||||
def clear(self):
|
def clear(self):
|
||||||
|
"""
|
||||||
|
Remove all items from the ViewBox.
|
||||||
|
"""
|
||||||
for i in self.items[:]:
|
for i in self.items[:]:
|
||||||
self.removeItem(i)
|
self.removeItem(i)
|
||||||
self.avgCurves = {}
|
self.avgCurves = {}
|
||||||
|
@ -37,7 +37,26 @@ class PlotROI(ROI):
|
|||||||
|
|
||||||
|
|
||||||
class ImageView(QtGui.QWidget):
|
class ImageView(QtGui.QWidget):
|
||||||
|
"""
|
||||||
|
Widget used for display and analysis of image data.
|
||||||
|
Implements many features:
|
||||||
|
|
||||||
|
* Displays 2D and 3D image data. For 3D data, a z-axis
|
||||||
|
slider is displayed allowing the user to select which frame is displayed.
|
||||||
|
* Displays histogram of image data with movable region defining the dark/light levels
|
||||||
|
* Editable gradient provides a color lookup table
|
||||||
|
* Frame slider may also be moved using left/right arrow keys as well as pgup, pgdn, home, and end.
|
||||||
|
* Basic analysis features including:
|
||||||
|
|
||||||
|
* ROI and embedded plot for measuring image values across frames
|
||||||
|
* Image normalization / background subtraction
|
||||||
|
|
||||||
|
Basic Usage::
|
||||||
|
|
||||||
|
imv = pg.ImageView()
|
||||||
|
imv.show()
|
||||||
|
imv.setImage(data)
|
||||||
|
"""
|
||||||
sigTimeChanged = QtCore.Signal(object, object)
|
sigTimeChanged = QtCore.Signal(object, object)
|
||||||
sigProcessingChanged = QtCore.Signal(object)
|
sigProcessingChanged = QtCore.Signal(object)
|
||||||
|
|
||||||
@ -149,7 +168,168 @@ class ImageView(QtGui.QWidget):
|
|||||||
|
|
||||||
self.roiClicked() ## initialize roi plot to correct shape / visibility
|
self.roiClicked() ## initialize roi plot to correct shape / visibility
|
||||||
|
|
||||||
|
def setImage(self, img, autoRange=True, autoLevels=True, levels=None, axes=None, xvals=None, pos=None, scale=None):
|
||||||
|
"""
|
||||||
|
Set the image to be displayed in the widget.
|
||||||
|
|
||||||
|
============== =======================================================================
|
||||||
|
**Arguments:**
|
||||||
|
*img* (numpy array) the image to be displayed.
|
||||||
|
*xvals* (numpy array) 1D array of z-axis values corresponding to the third axis
|
||||||
|
in a 3D image. For video, this array should contain the time of each frame.
|
||||||
|
*autoRange* (bool) whether to scale/pan the view to fit the image.
|
||||||
|
*autoLevels* (bool) whether to update the white/black levels to fit the image.
|
||||||
|
*levels* (min, max); the white and black level values to use.
|
||||||
|
*axes* Dictionary indicating the interpretation for each axis.
|
||||||
|
This is only needed to override the default guess. Format is::
|
||||||
|
|
||||||
|
{'t':0, 'x':1, 'y':2, 'c':3};
|
||||||
|
============== =======================================================================
|
||||||
|
"""
|
||||||
|
prof = debug.Profiler('ImageView.setImage', disabled=True)
|
||||||
|
|
||||||
|
if not isinstance(img, np.ndarray):
|
||||||
|
raise Exception("Image must be specified as ndarray.")
|
||||||
|
self.image = img
|
||||||
|
|
||||||
|
if xvals is not None:
|
||||||
|
self.tVals = xvals
|
||||||
|
elif hasattr(img, 'xvals'):
|
||||||
|
try:
|
||||||
|
self.tVals = img.xvals(0)
|
||||||
|
except:
|
||||||
|
self.tVals = np.arange(img.shape[0])
|
||||||
|
else:
|
||||||
|
self.tVals = np.arange(img.shape[0])
|
||||||
|
#self.ui.timeSlider.setValue(0)
|
||||||
|
#self.ui.normStartSlider.setValue(0)
|
||||||
|
#self.ui.timeSlider.setMaximum(img.shape[0]-1)
|
||||||
|
prof.mark('1')
|
||||||
|
|
||||||
|
if axes is None:
|
||||||
|
if img.ndim == 2:
|
||||||
|
self.axes = {'t': None, 'x': 0, 'y': 1, 'c': None}
|
||||||
|
elif img.ndim == 3:
|
||||||
|
if img.shape[2] <= 4:
|
||||||
|
self.axes = {'t': None, 'x': 0, 'y': 1, 'c': 2}
|
||||||
|
else:
|
||||||
|
self.axes = {'t': 0, 'x': 1, 'y': 2, 'c': None}
|
||||||
|
elif img.ndim == 4:
|
||||||
|
self.axes = {'t': 0, 'x': 1, 'y': 2, 'c': 3}
|
||||||
|
else:
|
||||||
|
raise Exception("Can not interpret image with dimensions %s" % (str(img.shape)))
|
||||||
|
elif isinstance(axes, dict):
|
||||||
|
self.axes = axes.copy()
|
||||||
|
elif isinstance(axes, list) or isinstance(axes, tuple):
|
||||||
|
self.axes = {}
|
||||||
|
for i in range(len(axes)):
|
||||||
|
self.axes[axes[i]] = i
|
||||||
|
else:
|
||||||
|
raise Exception("Can not interpret axis specification %s. Must be like {'t': 2, 'x': 0, 'y': 1} or ('t', 'x', 'y', 'c')" % (str(axes)))
|
||||||
|
|
||||||
|
for x in ['t', 'x', 'y', 'c']:
|
||||||
|
self.axes[x] = self.axes.get(x, None)
|
||||||
|
prof.mark('2')
|
||||||
|
|
||||||
|
self.imageDisp = None
|
||||||
|
|
||||||
|
|
||||||
|
prof.mark('3')
|
||||||
|
|
||||||
|
self.currentIndex = 0
|
||||||
|
self.updateImage()
|
||||||
|
if levels is None and autoLevels:
|
||||||
|
self.autoLevels()
|
||||||
|
if levels is not None: ## this does nothing since getProcessedImage sets these values again.
|
||||||
|
self.levelMax = levels[1]
|
||||||
|
self.levelMin = levels[0]
|
||||||
|
|
||||||
|
if self.ui.roiBtn.isChecked():
|
||||||
|
self.roiChanged()
|
||||||
|
prof.mark('4')
|
||||||
|
|
||||||
|
|
||||||
|
if self.axes['t'] is not None:
|
||||||
|
#self.ui.roiPlot.show()
|
||||||
|
self.ui.roiPlot.setXRange(self.tVals.min(), self.tVals.max())
|
||||||
|
self.timeLine.setValue(0)
|
||||||
|
#self.ui.roiPlot.setMouseEnabled(False, False)
|
||||||
|
if len(self.tVals) > 1:
|
||||||
|
start = self.tVals.min()
|
||||||
|
stop = self.tVals.max() + abs(self.tVals[-1] - self.tVals[0]) * 0.02
|
||||||
|
elif len(self.tVals) == 1:
|
||||||
|
start = self.tVals[0] - 0.5
|
||||||
|
stop = self.tVals[0] + 0.5
|
||||||
|
else:
|
||||||
|
start = 0
|
||||||
|
stop = 1
|
||||||
|
for s in [self.timeLine, self.normRgn]:
|
||||||
|
s.setBounds([start, stop])
|
||||||
|
#else:
|
||||||
|
#self.ui.roiPlot.hide()
|
||||||
|
prof.mark('5')
|
||||||
|
|
||||||
|
self.imageItem.resetTransform()
|
||||||
|
if scale is not None:
|
||||||
|
self.imageItem.scale(*scale)
|
||||||
|
if pos is not None:
|
||||||
|
self.imageItem.setPos(*pos)
|
||||||
|
prof.mark('6')
|
||||||
|
|
||||||
|
if autoRange:
|
||||||
|
self.autoRange()
|
||||||
|
self.roiClicked()
|
||||||
|
prof.mark('7')
|
||||||
|
prof.finish()
|
||||||
|
|
||||||
|
|
||||||
|
def play(self, rate):
|
||||||
|
"""Begin automatically stepping frames forward at the given rate (in fps).
|
||||||
|
This can also be accessed by pressing the spacebar."""
|
||||||
|
#print "play:", rate
|
||||||
|
self.playRate = rate
|
||||||
|
if rate == 0:
|
||||||
|
self.playTimer.stop()
|
||||||
|
return
|
||||||
|
|
||||||
|
self.lastPlayTime = ptime.time()
|
||||||
|
if not self.playTimer.isActive():
|
||||||
|
self.playTimer.start(16)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def autoLevels(self):
|
||||||
|
"""Set the min/max levels automatically to match the image data."""
|
||||||
|
#image = self.getProcessedImage()
|
||||||
|
self.setLevels(self.levelMin, self.levelMax)
|
||||||
|
|
||||||
|
#self.ui.histogram.imageChanged(autoLevel=True)
|
||||||
|
|
||||||
|
|
||||||
|
def setLevels(self, min, max):
|
||||||
|
"""Set the min/max (bright and dark) levels."""
|
||||||
|
self.ui.histogram.setLevels(min, max)
|
||||||
|
|
||||||
|
def autoRange(self):
|
||||||
|
"""Auto scale and pan the view around the image."""
|
||||||
|
image = self.getProcessedImage()
|
||||||
|
|
||||||
|
#self.ui.graphicsView.setRange(QtCore.QRectF(0, 0, image.shape[self.axes['x']], image.shape[self.axes['y']]), padding=0., lockAspect=True)
|
||||||
|
self.view.setRange(self.imageItem.boundingRect(), padding=0.)
|
||||||
|
|
||||||
|
def getProcessedImage(self):
|
||||||
|
"""Returns the image data after it has been processed by any normalization options in use."""
|
||||||
|
if self.imageDisp is None:
|
||||||
|
image = self.normalize(self.image)
|
||||||
|
self.imageDisp = image
|
||||||
|
self.levelMin, self.levelMax = map(float, ImageView.quickMinMax(self.imageDisp))
|
||||||
|
self.ui.histogram.setHistogramRange(self.levelMin, self.levelMax)
|
||||||
|
|
||||||
|
return self.imageDisp
|
||||||
|
|
||||||
|
|
||||||
def close(self):
|
def close(self):
|
||||||
|
"""Closes the widget nicely, making sure to clear the graphics scene and release memory."""
|
||||||
self.ui.roiPlot.close()
|
self.ui.roiPlot.close()
|
||||||
self.ui.graphicsView.close()
|
self.ui.graphicsView.close()
|
||||||
#self.ui.gradientWidget.sigGradientChanged.disconnect(self.updateImage)
|
#self.ui.gradientWidget.sigGradientChanged.disconnect(self.updateImage)
|
||||||
@ -224,17 +404,6 @@ class ImageView(QtGui.QWidget):
|
|||||||
else:
|
else:
|
||||||
self.play(0)
|
self.play(0)
|
||||||
|
|
||||||
def play(self, rate):
|
|
||||||
#print "play:", rate
|
|
||||||
self.playRate = rate
|
|
||||||
if rate == 0:
|
|
||||||
self.playTimer.stop()
|
|
||||||
return
|
|
||||||
|
|
||||||
self.lastPlayTime = ptime.time()
|
|
||||||
if not self.playTimer.isActive():
|
|
||||||
self.playTimer.start(16)
|
|
||||||
|
|
||||||
|
|
||||||
def timeout(self):
|
def timeout(self):
|
||||||
now = ptime.time()
|
now = ptime.time()
|
||||||
@ -251,6 +420,7 @@ class ImageView(QtGui.QWidget):
|
|||||||
self.jumpFrames(n)
|
self.jumpFrames(n)
|
||||||
|
|
||||||
def setCurrentIndex(self, ind):
|
def setCurrentIndex(self, ind):
|
||||||
|
"""Set the currently displayed frame index."""
|
||||||
self.currentIndex = np.clip(ind, 0, self.getProcessedImage().shape[0]-1)
|
self.currentIndex = np.clip(ind, 0, self.getProcessedImage().shape[0]-1)
|
||||||
self.updateImage()
|
self.updateImage()
|
||||||
self.ignoreTimeLine = True
|
self.ignoreTimeLine = True
|
||||||
@ -258,7 +428,7 @@ class ImageView(QtGui.QWidget):
|
|||||||
self.ignoreTimeLine = False
|
self.ignoreTimeLine = False
|
||||||
|
|
||||||
def jumpFrames(self, n):
|
def jumpFrames(self, n):
|
||||||
"""If this is a video, move ahead n frames"""
|
"""Move video frame ahead n frames (may be negative)"""
|
||||||
if self.axes['t'] is not None:
|
if self.axes['t'] is not None:
|
||||||
self.setCurrentIndex(self.currentIndex + n)
|
self.setCurrentIndex(self.currentIndex + n)
|
||||||
|
|
||||||
@ -360,137 +530,6 @@ class ImageView(QtGui.QWidget):
|
|||||||
|
|
||||||
#self.ui.roiPlot.replot()
|
#self.ui.roiPlot.replot()
|
||||||
|
|
||||||
def setImage(self, img, autoRange=True, autoLevels=True, levels=None, axes=None, xvals=None, pos=None, scale=None):
|
|
||||||
"""Set the image to be displayed in the widget.
|
|
||||||
Options are:
|
|
||||||
img: ndarray; the image to be displayed.
|
|
||||||
autoRange: bool; whether to scale/pan the view to fit the image.
|
|
||||||
autoLevels: bool; whether to update the white/black levels to fit the image.
|
|
||||||
levels: (min, max); the white and black level values to use.
|
|
||||||
axes: {'t':0, 'x':1, 'y':2, 'c':3}; Dictionary indicating the interpretation for each axis.
|
|
||||||
This is only needed to override the default guess.
|
|
||||||
"""
|
|
||||||
prof = debug.Profiler('ImageView.setImage', disabled=True)
|
|
||||||
|
|
||||||
if not isinstance(img, np.ndarray):
|
|
||||||
raise Exception("Image must be specified as ndarray.")
|
|
||||||
self.image = img
|
|
||||||
|
|
||||||
if xvals is not None:
|
|
||||||
self.tVals = xvals
|
|
||||||
elif hasattr(img, 'xvals'):
|
|
||||||
try:
|
|
||||||
self.tVals = img.xvals(0)
|
|
||||||
except:
|
|
||||||
self.tVals = np.arange(img.shape[0])
|
|
||||||
else:
|
|
||||||
self.tVals = np.arange(img.shape[0])
|
|
||||||
#self.ui.timeSlider.setValue(0)
|
|
||||||
#self.ui.normStartSlider.setValue(0)
|
|
||||||
#self.ui.timeSlider.setMaximum(img.shape[0]-1)
|
|
||||||
prof.mark('1')
|
|
||||||
|
|
||||||
if axes is None:
|
|
||||||
if img.ndim == 2:
|
|
||||||
self.axes = {'t': None, 'x': 0, 'y': 1, 'c': None}
|
|
||||||
elif img.ndim == 3:
|
|
||||||
if img.shape[2] <= 4:
|
|
||||||
self.axes = {'t': None, 'x': 0, 'y': 1, 'c': 2}
|
|
||||||
else:
|
|
||||||
self.axes = {'t': 0, 'x': 1, 'y': 2, 'c': None}
|
|
||||||
elif img.ndim == 4:
|
|
||||||
self.axes = {'t': 0, 'x': 1, 'y': 2, 'c': 3}
|
|
||||||
else:
|
|
||||||
raise Exception("Can not interpret image with dimensions %s" % (str(img.shape)))
|
|
||||||
elif isinstance(axes, dict):
|
|
||||||
self.axes = axes.copy()
|
|
||||||
elif isinstance(axes, list) or isinstance(axes, tuple):
|
|
||||||
self.axes = {}
|
|
||||||
for i in range(len(axes)):
|
|
||||||
self.axes[axes[i]] = i
|
|
||||||
else:
|
|
||||||
raise Exception("Can not interpret axis specification %s. Must be like {'t': 2, 'x': 0, 'y': 1} or ('t', 'x', 'y', 'c')" % (str(axes)))
|
|
||||||
|
|
||||||
for x in ['t', 'x', 'y', 'c']:
|
|
||||||
self.axes[x] = self.axes.get(x, None)
|
|
||||||
prof.mark('2')
|
|
||||||
|
|
||||||
self.imageDisp = None
|
|
||||||
|
|
||||||
|
|
||||||
prof.mark('3')
|
|
||||||
|
|
||||||
self.currentIndex = 0
|
|
||||||
self.updateImage()
|
|
||||||
if levels is None and autoLevels:
|
|
||||||
self.autoLevels()
|
|
||||||
if levels is not None: ## this does nothing since getProcessedImage sets these values again.
|
|
||||||
self.levelMax = levels[1]
|
|
||||||
self.levelMin = levels[0]
|
|
||||||
|
|
||||||
if self.ui.roiBtn.isChecked():
|
|
||||||
self.roiChanged()
|
|
||||||
prof.mark('4')
|
|
||||||
|
|
||||||
|
|
||||||
if self.axes['t'] is not None:
|
|
||||||
#self.ui.roiPlot.show()
|
|
||||||
self.ui.roiPlot.setXRange(self.tVals.min(), self.tVals.max())
|
|
||||||
self.timeLine.setValue(0)
|
|
||||||
#self.ui.roiPlot.setMouseEnabled(False, False)
|
|
||||||
if len(self.tVals) > 1:
|
|
||||||
start = self.tVals.min()
|
|
||||||
stop = self.tVals.max() + abs(self.tVals[-1] - self.tVals[0]) * 0.02
|
|
||||||
elif len(self.tVals) == 1:
|
|
||||||
start = self.tVals[0] - 0.5
|
|
||||||
stop = self.tVals[0] + 0.5
|
|
||||||
else:
|
|
||||||
start = 0
|
|
||||||
stop = 1
|
|
||||||
for s in [self.timeLine, self.normRgn]:
|
|
||||||
s.setBounds([start, stop])
|
|
||||||
#else:
|
|
||||||
#self.ui.roiPlot.hide()
|
|
||||||
prof.mark('5')
|
|
||||||
|
|
||||||
self.imageItem.resetTransform()
|
|
||||||
if scale is not None:
|
|
||||||
self.imageItem.scale(*scale)
|
|
||||||
if pos is not None:
|
|
||||||
self.imageItem.setPos(*pos)
|
|
||||||
prof.mark('6')
|
|
||||||
|
|
||||||
if autoRange:
|
|
||||||
self.autoRange()
|
|
||||||
self.roiClicked()
|
|
||||||
prof.mark('7')
|
|
||||||
prof.finish()
|
|
||||||
|
|
||||||
|
|
||||||
def autoLevels(self):
|
|
||||||
#image = self.getProcessedImage()
|
|
||||||
self.setLevels(self.levelMin, self.levelMax)
|
|
||||||
|
|
||||||
#self.ui.histogram.imageChanged(autoLevel=True)
|
|
||||||
|
|
||||||
|
|
||||||
def setLevels(self, min, max):
|
|
||||||
self.ui.histogram.setLevels(min, max)
|
|
||||||
|
|
||||||
def autoRange(self):
|
|
||||||
image = self.getProcessedImage()
|
|
||||||
|
|
||||||
#self.ui.graphicsView.setRange(QtCore.QRectF(0, 0, image.shape[self.axes['x']], image.shape[self.axes['y']]), padding=0., lockAspect=True)
|
|
||||||
self.view.setRange(self.imageItem.boundingRect(), padding=0.)
|
|
||||||
|
|
||||||
def getProcessedImage(self):
|
|
||||||
if self.imageDisp is None:
|
|
||||||
image = self.normalize(self.image)
|
|
||||||
self.imageDisp = image
|
|
||||||
self.levelMin, self.levelMax = map(float, ImageView.quickMinMax(self.imageDisp))
|
|
||||||
self.ui.histogram.setHistogramRange(self.levelMin, self.levelMax)
|
|
||||||
|
|
||||||
return self.imageDisp
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def quickMinMax(data):
|
def quickMinMax(data):
|
||||||
@ -578,7 +617,7 @@ class ImageView(QtGui.QWidget):
|
|||||||
|
|
||||||
|
|
||||||
def timeIndex(self, slider):
|
def timeIndex(self, slider):
|
||||||
"""Return the time and frame index indicated by a slider"""
|
## Return the time and frame index indicated by a slider
|
||||||
if self.image is None:
|
if self.image is None:
|
||||||
return (0,0)
|
return (0,0)
|
||||||
#v = slider.value()
|
#v = slider.value()
|
||||||
|
@ -16,11 +16,30 @@ class PlotWidget(GraphicsView):
|
|||||||
#sigRangeChanged = QtCore.Signal(object, object) ## already defined in GraphicsView
|
#sigRangeChanged = QtCore.Signal(object, object) ## already defined in GraphicsView
|
||||||
|
|
||||||
"""
|
"""
|
||||||
Widget implementing a graphicsView with a single PlotItem inside.
|
:class:`GraphicsView <pyqtgraph.GraphicsView>` widget with a single
|
||||||
|
:class:`PlotItem <pyqtgraph.PlotItem>` inside.
|
||||||
|
|
||||||
The following methods are wrapped directly from PlotItem: addItem, removeItem,
|
The following methods are wrapped directly from PlotItem:
|
||||||
clear, setXRange, setYRange, setRange, setAspectLocked, setMouseEnabled. For all
|
:func:`addItem <pyqtgraph.PlotItem.addItem>`,
|
||||||
other methods, use getPlotItem.
|
:func:`removeItem <pyqtgraph.PlotItem.removeItem>`,
|
||||||
|
:func:`clear <pyqtgraph.PlotItem.clear>`,
|
||||||
|
:func:`setXRange <pyqtgraph.ViewBox.setXRange>`,
|
||||||
|
:func:`setYRange <pyqtgraph.ViewBox.setYRange>`,
|
||||||
|
:func:`setRange <pyqtgraph.ViewBox.setRange>`,
|
||||||
|
:func:`autoRange <pyqtgraph.ViewBox.autoRange>`,
|
||||||
|
:func:`setXLink <pyqtgraph.ViewBox.setXLink>`,
|
||||||
|
:func:`setYLink <pyqtgraph.ViewBox.setYLink>`,
|
||||||
|
:func:`viewRect <pyqtgraph.ViewBox.viewRect>`,
|
||||||
|
:func:`setMouseEnabled <pyqtgraph.ViewBox.setMouseEnabled>`,
|
||||||
|
:func:`enableAutoRange <pyqtgraph.ViewBox.enableAutoRange>`,
|
||||||
|
:func:`disableAutoRange <pyqtgraph.ViewBox.disableAutoRange>`,
|
||||||
|
:func:`setAspectLocked <pyqtgraph.ViewBox.setAspectLocked>`,
|
||||||
|
:func:`register <pyqtgraph.ViewBox.register>`,
|
||||||
|
:func:`unregister <pyqtgraph.ViewBox.unregister>`
|
||||||
|
|
||||||
|
|
||||||
|
For all
|
||||||
|
other methods, use :func:`getPlotItem <pyqtgraph.PlotWidget.getPlotItem>`.
|
||||||
"""
|
"""
|
||||||
def __init__(self, parent=None, **kargs):
|
def __init__(self, parent=None, **kargs):
|
||||||
GraphicsView.__init__(self, parent)
|
GraphicsView.__init__(self, parent)
|
||||||
@ -29,7 +48,8 @@ class PlotWidget(GraphicsView):
|
|||||||
self.plotItem = PlotItem(**kargs)
|
self.plotItem = PlotItem(**kargs)
|
||||||
self.setCentralItem(self.plotItem)
|
self.setCentralItem(self.plotItem)
|
||||||
## Explicitly wrap methods from plotItem
|
## Explicitly wrap methods from plotItem
|
||||||
for m in ['addItem', 'removeItem', 'autoRange', 'clear', 'setXRange', 'setYRange', 'setRange', 'setAspectLocked', 'setMouseEnabled']:
|
## NOTE: If you change this list, update the documentation above as well.
|
||||||
|
for m in ['addItem', 'removeItem', 'autoRange', 'clear', 'setXRange', 'setYRange', 'setRange', 'setAspectLocked', 'setMouseEnabled', 'setXLink', 'setYLink', 'enableAutoRange', 'disableAutoRange', 'register', 'unregister', 'viewRect']:
|
||||||
setattr(self, m, getattr(self.plotItem, m))
|
setattr(self, m, getattr(self.plotItem, m))
|
||||||
#QtCore.QObject.connect(self.plotItem, QtCore.SIGNAL('viewChanged'), self.viewChanged)
|
#QtCore.QObject.connect(self.plotItem, QtCore.SIGNAL('viewChanged'), self.viewChanged)
|
||||||
self.plotItem.sigRangeChanged.connect(self.viewRangeChanged)
|
self.plotItem.sigRangeChanged.connect(self.viewRangeChanged)
|
||||||
|
Loading…
Reference in New Issue
Block a user