Advertisement
Guest User

mbe-issue

a guest
Oct 23rd, 2016
303
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 65.19 KB | None | 0 0
  1. # -*- coding: utf-8 -*-
  2. # !/usr/bin/env python3
  3.  
  4. from pyqtgraph.Qt import QtCore, QtGui, USE_PYSIDE
  5. import pyqtgraph.graphicsItems.ROI as pgROI
  6. from pyqtgraph.Point import Point
  7. #----------------ROI-------------------#
  8.  
  9.  
  10. class Handle(pgROI.Handle):
  11.     """ Sub-class of pyqtgraph ROI handle. Main purpose is to deactivate the hoverEvent,
  12.        mouseClickEvent and mouseDragEvent when the associated ROI is not active. This is
  13.        required to allow ROIs to be drawn over the top of each other """
  14.  
  15.     def __init__(self, radius, typ=None, pen=(200, 200, 220), parent=None, deletable=False):
  16.         pgROI.Handle.__init__(self, radius, typ, pen, parent, deletable)
  17.         self.setSelectable(True)
  18.  
  19.     def setSelectable(self, isActive):
  20.         self.isActive = isActive
  21.  
  22.     def raiseContextMenu(self, ev):
  23.         menu = self.getMenu()
  24.  
  25.         ## Make sure it is still ok to remove this handle
  26.         removeAllowed = all([r.checkRemoveHandle(self) for r in self.rois])
  27.         self.removeAction.setEnabled(removeAllowed)
  28.         pos = ev.screenPos()
  29.         menu.popup(QtCore.QPoint(pos.x(), pos.y()))
  30.  
  31.     def hoverEvent(self, ev):
  32.         # Ignore events if handle is not active
  33.         if not self.isActive: return
  34.         hover = False
  35.         if not ev.isExit():
  36.             if ev.acceptDrags(QtCore.Qt.LeftButton):
  37.                 hover = True
  38.             for btn in [QtCore.Qt.LeftButton, QtCore.Qt.RightButton, QtCore.Qt.MidButton]:
  39.                 if int(self.acceptedMouseButtons() & btn) > 0 and ev.acceptClicks(btn):
  40.                     hover = True
  41.         if hover:
  42.             self.currentPen = fn.mkPen(255, 255, 0)
  43.         else:
  44.             self.currentPen = self.pen
  45.         self.update()
  46.  
  47.     def mouseClickEvent(self, ev):
  48.         # Ignore events if handle is not active
  49.         if not self.isActive:
  50.             ev.ignore()
  51.             return
  52.  
  53.         if ev.button() == QtCore.Qt.RightButton and self.isMoving:
  54.             self.isMoving = False
  55.             self.movePoint(self.startPos, finish=True)
  56.             ev.accept()
  57.         elif int(ev.button() & self.acceptedMouseButtons()) > 0:
  58.             ev.accept()
  59.             if ev.button() == QtCore.Qt.RightButton and self.deletable:
  60.                 self.raiseContextMenu(ev)
  61.             self.sigClicked.emit(self, ev)
  62.         else:
  63.             ev.ignore()
  64.  
  65.     def mouseDragEvent(self, ev):
  66.         # Ignore events if handle is not active
  67.         if not self.isActive: return
  68.  
  69.         if ev.button() != QtCore.Qt.LeftButton:
  70.             return
  71.         ev.accept()
  72.  
  73.         if ev.isFinish():
  74.             if self.isMoving:
  75.                 for r in self.rois:
  76.                     r.stateChangeFinished()
  77.             self.isMoving = False
  78.         elif ev.isStart():
  79.             for r in self.rois:
  80.                 r.handleMoveStarted()
  81.             self.isMoving = True
  82.             self.startPos = self.scenePos()
  83.             self.cursorOffset = self.scenePos() - ev.buttonDownScenePos()
  84.  
  85.         if self.isMoving:
  86.             pos = ev.scenePos() + self.cursorOffset
  87.             self.movePoint(pos, ev.modifiers(), finish=False)
  88.  
  89.  
  90. class ROI(pgROI.ROI):
  91.     """ Sub-class of pyqtgraph ROI class. Main purpose is to customise the ROI handles """
  92.  
  93.     def __init__(self, pos, size=Point(1, 1), angle=0.0, invertible=False, maxBounds=None, snapSize=1.0,
  94.                  scaleSnap=False,
  95.                  translateSnap=False, rotateSnap=False, parent=None, pen=None, movable=True, removable=False):
  96.         pgROI.ROI.__init__(self, pos, size, angle, invertible, maxBounds, snapSize, scaleSnap, translateSnap,
  97.                            rotateSnap, parent, pen, movable, removable)
  98.  
  99.     def addHandle(self, info, index=None):
  100.  
  101.         ## If a Handle was not supplied, create it now
  102.         if 'item' not in info or info['item'] is None:
  103.             h = Handle(self.handleSize, typ=info['type'], pen=self.handlePen, parent=self)
  104.             h.setPos(info['pos'] * self.state['size'])
  105.             info['item'] = h
  106.         else:
  107.             h = info['item']
  108.             if info['pos'] is None:
  109.                 info['pos'] = h.pos()
  110.  
  111.         ## Connect the handle to this ROI
  112.         h.connectROI(self)
  113.         if index is None:
  114.             self.handles.append(info)
  115.         else:
  116.             self.handles.insert(index, info)
  117.  
  118.         h.setZValue(self.zValue() + 1)
  119.         self.stateChanged()
  120.         return h
  121.  
  122.     def paint(self, p, opt, widget):
  123.         p.save()
  124.         r = self.boundingRect()
  125.         # p.setRenderHint(QtGui.QPainter.Antialiasing)
  126.         p.setPen(self.currentPen)
  127.         p.translate(r.left(), r.top())
  128.         p.scale(r.width(), r.height())
  129.         p.drawRect(0, 0, 1, 1)
  130.         p.restore()
  131.  
  132.  
  133. class PolylineSegment(pgROI.LineSegmentROI):
  134.     """ Sub-class of pyqtgraph LineSegmentROI class. Main purpose is to deactivate functions if not active """
  135.  
  136.     def __init__(self, positions=(None, None), pos=None, handles=(None, None), **args):
  137.         pgROI.LineSegmentROI.__init__(self, positions, pos, handles, **args)
  138.  
  139.         self.penHover = fn.mkPen(255, 255, 0)
  140.         self.translatable = False
  141.         self.setSelectable(True)
  142.         self.setAcceptsHandles(True)
  143.  
  144.     def setSelectable(self, isActive):
  145.         self.isActive = isActive
  146.         if isActive:
  147.             self.setAcceptedMouseButtons(QtCore.Qt.LeftButton)
  148.         else:
  149.             self.setAcceptedMouseButtons(QtCore.Qt.NoButton)
  150.  
  151.     def setAcceptsHandles(self, acceptsHandles):
  152.         self.acceptsHandles = acceptsHandles
  153.         if acceptsHandles:
  154.             self.setAcceptedMouseButtons(QtCore.Qt.LeftButton)
  155.         else:
  156.             self.setAcceptedMouseButtons(QtCore.Qt.NoButton)
  157.  
  158.     def hoverEvent(self, ev):
  159.         if not self.isActive: return
  160.         if (self.translatable or self.acceptsHandles) and (not ev.isExit()) and ev.acceptDrags(QtCore.Qt.LeftButton):
  161.             self.setMouseHover(True)
  162.             self.sigHoverEvent.emit(self)
  163.         else:
  164.             self.setMouseHover(False)
  165.  
  166.     def setMouseHover(self, hover):
  167.         ## Inform the ROI that the mouse is(not) hovering over it
  168.         # if self.mouseHovering == hover:
  169.         #    return
  170.         # self.mouseHovering = hover
  171.         if hover:
  172.             self.currentPen = self.penHover
  173.         else:
  174.             self.currentPen = self.pen
  175.         self.update()
  176.  
  177.     def paint(self, p, *args):
  178.         # p.setRenderHint(QtGui.QPainter.Antialiasing)
  179.         p.setPen(self.currentPen)
  180.         h1 = self.handles[0]['item'].pos()
  181.         h2 = self.handles[1]['item'].pos()
  182.         p.drawLine(h1, h2)
  183.  
  184.  
  185. class selectableROI(object):
  186.     """ A ROI class that can be used with a multiRoiViewbox """
  187.  
  188.     sigRemoveRequested = QtCore.Signal(object)
  189.     sigCopyRequested = QtCore.Signal(object)
  190.     sigSaveRequested = QtCore.Signal(object)
  191.  
  192.     def __init__(self):
  193.         self.penActive = fn.mkPen(0, 255, 0)
  194.         self.penInactive = fn.mkPen(255, 0, 0)
  195.         self.penHover = fn.mkPen(255, 255, 0)
  196.         self.penActive.setWidth(1)
  197.         self.penInactive.setWidth(1)
  198.         self.penHover.setWidth(1)
  199.         self.setName(str(uuid.uuid4()))
  200.         self.isSelected = False
  201.         self.menu = None
  202.         self.setActive(True)
  203.  
  204.     def set_color(self, r, g, b):
  205.         self.penActive.setColor(QtGui.QColor(r, g, b))
  206.  
  207.     def setActive(self, isActive):
  208.         self.isActive = isActive
  209.         if isActive:
  210.             self.setAcceptedMouseButtons(QtCore.Qt.LeftButton or QtCore.Qt.RightButton)
  211.         else:
  212.             self.setAcceptedMouseButtons(QtCore.Qt.NoButton)
  213.  
  214.     def __lt__(self, other):
  215.         selfid = int(self.name.split('-')[-1])
  216.         otherid = int(other.name.split('-')[-1])
  217.         return selfid < otherid
  218.  
  219.     def setName(self, name):
  220.         if name is not None:
  221.             self.name = name
  222.         else:
  223.             raise ValueError('No ROI name')
  224.  
  225.     def removeClicked(self):
  226.         self.sigRemoveRequested.emit(self)
  227.  
  228.     def copyClicked(self):
  229.         self.sigCopyRequested.emit(self)
  230.  
  231.     def saveClicked(self):
  232.         self.sigSaveRequested.emit(self)
  233.  
  234.     def hoverEvent(self, ev):
  235.         hover = False
  236.         if not ev.isExit():
  237.             if self.translatable and ev.acceptDrags(QtCore.Qt.LeftButton):
  238.                 hover = True
  239.             for btn in [QtCore.Qt.LeftButton, QtCore.Qt.RightButton, QtCore.Qt.MidButton]:
  240.                 if int(self.acceptedMouseButtons() & btn) > 0 and ev.acceptClicks(btn):
  241.                     hover = True
  242.             if self.contextMenuEnabled():
  243.                 ev.acceptClicks(QtCore.Qt.RightButton)
  244.  
  245.         if hover:
  246.             self.setMouseHover(True)
  247.             self.sigHoverEvent.emit(self)
  248.             ev.acceptClicks(QtCore.Qt.RightButton)
  249.         else:
  250.             self.setMouseHover(False)
  251.  
  252.     def mouseDragEvent(self, ev):
  253.         if ev.isStart():
  254.             # Drag using left button only if selected
  255.             if ev.button() == QtCore.Qt.LeftButton:
  256.                 if self.translatable:
  257.                     self.isMoving = True
  258.                     self.preMoveState = self.getState()
  259.                     self.cursorOffset = self.pos() - self.mapToParent(ev.buttonDownPos())
  260.                     self.sigRegionChangeStarted.emit(self)
  261.                     ev.accept()
  262.                 else:
  263.                     ev.ignore()
  264.         elif ev.isFinish():
  265.             if self.translatable:
  266.                 if self.isMoving:
  267.                     self.stateChangeFinished()
  268.                 self.isMoving = False
  269.             return
  270.         if self.translatable and self.isMoving and ev.buttons() == QtCore.Qt.LeftButton:
  271.             snap = True if (ev.modifiers() & QtCore.Qt.ControlModifier) else None
  272.             newPos = self.mapToParent(ev.pos()) + self.cursorOffset
  273.             self.translate(newPos - self.pos(), snap=snap, finish=False)
  274.  
  275.     def mouseClickEvent(self, ev):
  276.         if ev.button() == QtCore.Qt.RightButton and self.isMoving:
  277.             ev.accept()
  278.             self.cancelMove()
  279.         elif ev.button() == QtCore.Qt.RightButton and self.contextMenuEnabled():
  280.             self.raiseContextMenu(ev)
  281.             ev.accept()
  282.         elif int(ev.button() & self.acceptedMouseButtons()) > 0:
  283.             ev.accept()
  284.             self.sigClicked.emit(self, ev)
  285.         else:
  286.             ev.ignore()
  287.  
  288.     def contextMenuEnabled(self):
  289.         return (self.removable and self.isActive)
  290.  
  291.     def raiseContextMenu(self, ev):
  292.         if not self.contextMenuEnabled():
  293.             return
  294.         menu = self.getMenu()
  295.         pos = ev.screenPos()
  296.         menu.popup(QtCore.QPoint(pos.x(), pos.y()))
  297.  
  298.     def getMenu(self):
  299.         # Setup menu
  300.         if self.menu is None:
  301.             self.menu = QMenuCustom()
  302.             self.menuTitle = QtGui.QAction(self.name, self.menu)
  303.             self.copyAct = QtGui.QAction("Copy", self.menu)
  304.             self.saveAct = QtGui.QAction("Save", self.menu)
  305.             self.remAct = QtGui.QAction("Remove", self.menu)
  306.             self.menu.actions = [self.menuTitle, self.copyAct, self.saveAct, self.remAct]
  307.             # Connect signals to actions
  308.             self.copyAct.triggered.connect(self.copyClicked)
  309.             self.remAct.triggered.connect(self.removeClicked)
  310.             self.saveAct.triggered.connect(self.saveClicked)
  311.             # Add actions to menu
  312.             self.menu.addAction(self.menuTitle)
  313.             self.menu.addSeparator()
  314.             for action in self.menu.actions[1:]:
  315.                 self.menu.addAction(action)
  316.             # Set default properties
  317.             self.menuTitle.setDisabled(True)
  318.             self.menu.setStyleSheet("QMenu::item {color: black; font-weight:normal;}")
  319.             font = QtGui.QFont()
  320.             font.setBold(True)
  321.             self.menuTitle.setFont(font)
  322.         # Enable menus only for selected roi
  323.         if self.isSelected:
  324.             self.copyAct.setVisible(True)
  325.             self.saveAct.setVisible(True)
  326.             self.remAct.setVisible(True)
  327.         else:
  328.             self.copyAct.setVisible(False)
  329.             self.saveAct.setVisible(False)
  330.             self.remAct.setVisible(False)
  331.         return self.menu
  332.  
  333.  
  334. class PolyLineROIcustom(selectableROI, ROI):
  335.     # class PolyLineROIcustom(selectableROI):
  336.  
  337.     sigRemoveRequested = QtCore.Signal(object)
  338.     sigCopyRequested = QtCore.Signal(object)
  339.     sigSaveRequested = QtCore.Signal(object)
  340.  
  341.     def __init__(self, pos=[0, 0], handlePositions=None, **args):
  342.         ROI.__init__(self, pos, size=[1, 1], **args)
  343.         selectableROI.__init__(self)
  344.  
  345.         self.segments = []
  346.         self.translatable = False
  347.         self.closed = True
  348.         self.pen = self.penActive
  349.         self.handlePen = self.penActive
  350.         self.createROI(handlePositions)
  351.         self.setActive(False)
  352.  
  353.     def createROI(self, handlePositions):
  354.         # Create ROI from handle positions. Used mainly for copy and save
  355.         if handlePositions is None: return
  356.         for hp in handlePositions:
  357.             self.addFreeHandle(hp)
  358.         for i in range(-1, len(self.handles) - 1):
  359.             self.addSegment(self.handles[i]['item'], self.handles[i + 1]['item'])
  360.  
  361.     def addSegment(self, h1, h2, index=None):
  362.         seg = PolylineSegment(handles=(h1, h2), pen=self.pen, parent=self, movable=False)
  363.         if index is None:
  364.             self.segments.append(seg)
  365.         else:
  366.             self.segments.insert(index, seg)
  367.         seg.sigClicked.connect(self.segmentClicked)
  368.         seg.setAcceptedMouseButtons(QtCore.Qt.LeftButton)
  369.         seg.setZValue(self.zValue() + 1)
  370.         for h in seg.handles:
  371.             h['item'].setDeletable(True)
  372.             h['item'].setAcceptedMouseButtons(h[
  373.                                                   'item'].acceptedMouseButtons() | QtCore.Qt.LeftButton)  ## have these handles take left clicks too, so that handles cannot be added on top of other handles
  374.  
  375.     def setMouseHover(self, hover):
  376.         # self.counter += 1
  377.         # print 'setMouseHover ', self.counter, " ", hover
  378.         ROI.setMouseHover(self, hover)
  379.         for s in self.segments:
  380.             s.setMouseHover(hover)
  381.         # print s
  382.         self.update()
  383.  
  384.     def addHandle(self, info, index=None):
  385.         h = ROI.addHandle(self, info, index=index)
  386.         h.sigRemoveRequested.connect(self.removeHandle)
  387.         return h
  388.  
  389.     def segmentClicked(self, segment, ev=None, pos=None):  ## pos should be in this item's coordinate system
  390.         if ev != None:
  391.             pos = segment.mapToParent(ev.pos())
  392.         elif pos != None:
  393.             pos = pos
  394.         else:
  395.             raise Exception("Either an event or a position must be given.")
  396.         h2 = segment.handles[1]['item']
  397.  
  398.         i = self.segments.index(segment)
  399.         h3 = self.addFreeHandle(pos, index=self.indexOfHandle(h2))
  400.         self.addSegment(h3, h2, index=i + 1)
  401.         segment.replaceHandle(h2, h3)
  402.  
  403.     def removeHandle(self, handle, updateSegments=True):
  404.         ROI.removeHandle(self, handle)
  405.         handle.sigRemoveRequested.disconnect(self.removeHandle)
  406.  
  407.         if not updateSegments:
  408.             return
  409.         segments = handle.rois[:]
  410.  
  411.         if len(segments) == 1:
  412.             self.removeSegment(segments[0])
  413.         else:
  414.             handles = [h['item'] for h in segments[1].handles]
  415.             handles.remove(handle)
  416.             segments[0].replaceHandle(handle, handles[0])
  417.             self.removeSegment(segments[1])
  418.  
  419.     def removeSegment(self, seg):
  420.         for handle in seg.handles[:]:
  421.             seg.removeHandle(handle['item'])
  422.         self.segments.remove(seg)
  423.         seg.sigClicked.disconnect(self.segmentClicked)
  424.         self.scene().removeItem(seg)
  425.  
  426.     def checkRemoveHandle(self, h):
  427.         if self.closed:
  428.             return len(self.handles) > 3
  429.         else:
  430.             return len(self.handles) > 2
  431.  
  432.     def paint(self, p, *args):
  433.         pass
  434.  
  435.     def boundingRect(self):
  436.         return self.shape().boundingRect()
  437.  
  438.     def shape(self):
  439.         p = QtGui.QPainterPath()
  440.         p.moveTo(self.handles[0]['item'].pos())
  441.         for i in range(len(self.handles)):
  442.             p.lineTo(self.handles[i]['item'].pos())
  443.         p.lineTo(self.handles[0]['item'].pos())
  444.         return p
  445.  
  446.     def setSelected(self, s):
  447.         QtGui.QGraphicsItem.setSelected(self, s)
  448.         if s:
  449.             self.isSelected = True
  450.             self.translatable = True
  451.             for seg in self.segments:
  452.                 seg.setPen(self.penActive)
  453.                 seg.setSelectable(True)
  454.                 seg.setAcceptsHandles(True)
  455.                 seg.update()
  456.             for h in self.handles:
  457.                 h['item'].currentPen = self.penActive
  458.                 h['item'].pen = h['item'].currentPen
  459.                 h['item'].show()
  460.                 h['item'].update()
  461.         else:
  462.             self.isSelected = False
  463.             self.translatable = False
  464.             for seg in self.segments:
  465.                 seg.setPen(self.penInactive)
  466.                 seg.setSelectable(False)
  467.                 seg.setAcceptsHandles(False)
  468.                 seg.update()
  469.             for h in self.handles:
  470.                 h['item'].currentPen = self.penInactive
  471.                 h['item'].pen = h['item'].currentPen
  472.                 h['item'].hide()
  473.                 h['item'].update()
  474.  
  475.     def getArrayRegion(self, data, img, axes=(0, 1), returnMappedCoords=False, **kwds):
  476.         sl = self.getArraySlice(data, img, axes=(0, 1))
  477.         if sl is None:
  478.             return None
  479.         sliced = data[sl[0]]
  480.         im = QtGui.QImage(sliced.shape[axes[0]], sliced.shape[axes[1]], QtGui.QImage.Format_ARGB32)
  481.         im.fill(0x0)
  482.         p = QtGui.QPainter(im)
  483.         p.setPen(fn.mkPen(None))
  484.         p.setBrush(fn.mkBrush('w'))
  485.         p.setTransform(self.itemTransform(img)[0])
  486.         bounds = self.mapRectToItem(img, self.boundingRect())
  487.         p.translate(-bounds.left(), -bounds.top())
  488.         p.drawPath(self.shape())
  489.         p.end()
  490.         mask = imageToArray(im)[:, :, 0].astype(float) / 255.
  491.         # Old code that doesn't seem to do what it should
  492.         # shape = [1] * data.ndim
  493.         # shape[axes[0]] = sliced.shape[axes[0]]
  494.         # shape[axes[1]] = sliced.shape[axes[1]]
  495.         # return sliced * mask.reshape(shape)
  496.  
  497.         # Return image with mask applied
  498.         reshaped_mask = mask.T[:, :, np.newaxis]
  499.         return sliced * reshaped_mask
  500.  
  501.     def getROIMask(self, data, img, axes=(0, 1), returnMappedCoords=False, **kwds):
  502.         # sl = self.getArraySlice(data, img, axes=(0, 1))
  503.         # if sl is None:
  504.         #    return None
  505.         # sliced = data[sl[0]]
  506.         sliced = data
  507.         im = QtGui.QImage(sliced.shape[axes[0]], sliced.shape[axes[1]], QtGui.QImage.Format_ARGB32)
  508.         im.fill(0x0)
  509.         p = QtGui.QPainter(im)
  510.         p.setPen(fn.mkPen(None))
  511.         p.setBrush(fn.mkBrush('w'))
  512.         p.setTransform(self.itemTransform(img)[0])
  513.         # No moving
  514.         # bounds = self.mapRectToItem(img, self.boundingRect())
  515.         # p.translate(-bounds.left(), -bounds.top())
  516.         p.drawPath(self.shape())
  517.         p.end()
  518.         mask = imageToArray(im)[:, :, 0].astype(float) / 255.
  519.         # Old code that doesn't seem to do what it should
  520.         # shape = [1] * data.ndim
  521.         # shape[axes[0]] = sliced.shape[axes[0]]
  522.         # shape[axes[1]] = sliced.shape[axes[1]]
  523.         # return sliced * mask.reshape(shape)
  524.  
  525.         # Return image with mask applied
  526.         # reshaped_mask = mask.T[:, :, np.newaxis]
  527.         return np.flipud(mask)
  528.  
  529.         # def getROISlice(self, data, img, axes=(0,1), returnMappedCoords=False, **kwds):
  530.         #     sl = self.getArraySlice(data, img, axes=(0, 1))
  531.         #     if sl is None:
  532.         #         return None
  533.         #     sliced = data[sl[0]]
  534.         #     im = QtGui.QImage(sliced.shape[axes[0]], sliced.shape[axes[1]], QtGui.QImage.Format_ARGB32)
  535.         #     im.fill(0x0)
  536.         #     p = QtGui.QPainter(im)
  537.         #     p.setPen(fn.mkPen(None))
  538.         #     p.setBrush(fn.mkBrush('w'))
  539.         #     p.setTransform(self.itemTransform(img)[0])
  540.         #     bounds = self.mapRectToItem(img, self.boundingRect())
  541.         #     p.translate(-bounds.left(), -bounds.top())
  542.         #     p.drawPath(self.shape())
  543.         #     p.end()
  544.         #     mask = imageToArray(im)[:,:,0].astype(float) / 255.
  545.         #     # Old code that doesn't seem to do what it should
  546.         #     #shape = [1] * data.ndim
  547.         #     #shape[axes[0]] = sliced.shape[axes[0]]
  548.         #     #shape[axes[1]] = sliced.shape[axes[1]]
  549.         #     #return sliced * mask.reshape(shape)
  550.         #
  551.         #     return sliced
  552.  
  553.  
  554. def imageToArray(img):
  555.     """
  556.    Replaces imageToArray in pyqtgraph functions.py. Due to errors in Python 2.6
  557.    Error appears to be due to numpy and converting ptr to arr. Early versions create
  558.    an array of empty strings and later versions create an array of the wrong size.
  559.    The following method works, although arr in read-only i.e. changing the values in
  560.    the arr will not change the pixel values in the original QImage.
  561.    """
  562.     fmt = img.format()
  563.     ptr = img.bits()
  564.     if USE_PYSIDE:
  565.         arr = np.frombuffer(ptr, dtype=np.ubyte)
  566.     else:
  567.         ptr.setsize(img.byteCount())
  568.         # buf = buffer(ptr, 0, img.byteCount())
  569.         buf = memoryview(ptr)
  570.         arr = np.frombuffer(buf, dtype=np.ubyte)
  571.     if fmt == img.Format_RGB32:
  572.         arr = arr.reshape(img.height(), img.width(), 3)
  573.     elif fmt == img.Format_ARGB32 or fmt == img.Format_ARGB32_Premultiplied:
  574.         arr = arr.reshape(img.height(), img.width(), 4)
  575.     return arr
  576.  
  577.  
  578. class RectROIcustom(selectableROI, ROI):
  579.     sigRemoveRequested = QtCore.Signal(object)
  580.     sigCopyRequested = QtCore.Signal(object)
  581.     sigSaveRequested = QtCore.Signal(object)
  582.  
  583.     def __init__(self, pos, size, angle, **args):
  584.         ROI.__init__(self, pos, size, angle, **args)
  585.         selectableROI.__init__(self)
  586.         self.addScaleHandle([0.0, 0.5], [1.0, 0.5])
  587.         self.addScaleHandle([1.0, 0.5], [0.0, 0.5])
  588.         self.addScaleHandle([0.5, 0.0], [0.5, 1.0])
  589.         self.addScaleHandle([0.5, 1.0], [0.5, 0.0])
  590.         self.addRotateHandle([0.0, 0.0], [1.0, 1.0])
  591.         self.addRotateHandle([0.0, 1.0], [1.0, 0.0])
  592.         self.addRotateHandle([1.0, 0.0], [0.0, 1.0])
  593.         self.addRotateHandle([1.0, 1.0], [0.0, 0.0])
  594.  
  595.     def setSelected(self, s):
  596.         QtGui.QGraphicsItem.setSelected(self, s)
  597.         if s:
  598.             self.isSelected = True
  599.             self.translatable = True
  600.             self.setPen(self.penActive)
  601.             for h in self.handles:
  602.                 h['item'].currentPen = self.penActive
  603.                 h['item'].pen = h['item'].currentPen
  604.                 h['item'].show()
  605.                 h['item'].update()
  606.         else:
  607.             self.isSelected = False
  608.             self.translatable = False
  609.             self.setPen(self.penInactive)
  610.             for h in self.handles:
  611.                 h['item'].currentPen = self.penInactive
  612.                 h['item'].pen = h['item'].currentPen
  613.                 h['item'].hide()
  614.                 h['item'].update()
  615.  
  616. from PyQt4.QtGui import *
  617. import pyqtgraph as pg
  618. # --------------------- Mygraphicsview ----------------------- #
  619. class MyGraphicsView(pg.GraphicsView):
  620.     def __init__(self, project, parent=None):
  621.         super(MyGraphicsView, self).__init__(parent)
  622.  
  623.         self.project = project
  624.         self.shape = 0, 0
  625.  
  626.         self.setup_ui()
  627.  
  628.         self.update()
  629.  
  630.     def setup_ui(self):
  631.         self.setMinimumSize(200, 200)
  632.         l = QGraphicsGridLayout()
  633.  
  634.         self.centralWidget.setLayout(l)
  635.         l.setHorizontalSpacing(0)
  636.         l.setVerticalSpacing(0)
  637.  
  638.         self.vb = MultiRoiViewBox(lockAspect=True, enableMenu=True)
  639.         self.vb.enableAutoRange()
  640.  
  641.         l.addItem(self.vb, 0, 1)
  642.         self.xScale = pg.AxisItem(orientation='bottom', linkView=self.vb)
  643.         self.xScale.setLabel(text="<span style='color: #ff0000; font-weight: bold'>X</span>"
  644.                                   "<i>Width</i>", units="mm")
  645.         l.addItem(self.xScale, 1, 1)
  646.  
  647.         self.yScale = pg.AxisItem(orientation='left', linkView=self.vb)
  648.         self.yScale.setLabel(text="<span style='color: #ff0000; font-weight: bold'>Y</span>"
  649.                                   "<i>Height</i>", units='mm')
  650.         l.addItem(self.yScale, 0, 0)
  651.  
  652.         self.centralWidget.setLayout(l)
  653.  
  654.     def show(self, frame):
  655.         self.shape = frame.shape
  656.         self.vb.showImage(frame)
  657.         self.update()
  658.  
  659.     def _update_rect(self):
  660.         w, h = self.shape[0], self.shape[1]
  661.         if not (not self.project):
  662.             ox, oy = self.project['origin']
  663.             mmpixel = self.project['mmpixel']
  664.  
  665.             x = -ox * mmpixel
  666.             y = -oy * mmpixel
  667.             w = w * mmpixel
  668.             h = h * mmpixel
  669.  
  670.             self.vb.update_rect(x, y, w, h)
  671.  
  672.     def update(self):
  673.         self._update_rect()
  674.  
  675. import pyqtgraph as pg
  676. from pyqtgraph.Qt import QtCore, QtGui
  677. import numpy as np
  678. import matplotlib.pyplot as plt
  679. import pickle
  680. import pyqtgraph.functions as fn
  681. import types
  682. # ---------------------------- viewboxcustom ------------------------- #
  683.  
  684. class UnsupportedRoiTypeError(Exception):
  685.     pass
  686.  
  687.  
  688. class ImageAnalysisViewBox(pg.ViewBox):
  689.     """
  690.    Custom ViewBox used to over-ride the context menu. I don't want the full context menu,
  691.    just a view all and an export. Export does not call a dialog, just prompts user for filename.
  692.    """
  693.  
  694.     def __init__(self, parent=None, border=None, lockAspect=False, enableMouse=True, invertY=False, enableMenu=True,
  695.                  name=None):
  696.         pg.ViewBox.__init__(self, parent, border, lockAspect, enableMouse, invertY, enableMenu, name)
  697.  
  698.         self.menu = None  # Override pyqtgraph ViewBoxMenu
  699.         self.menu = self.getMenu(None)
  700.  
  701.     def raiseContextMenu(self, ev):
  702.         if not self.menuEnabled(): return
  703.         menu = self.getMenu(ev)
  704.         pos = ev.screenPos()
  705.         menu.popup(QtCore.QPoint(pos.x(), pos.y()))
  706.  
  707.     def export(self):
  708.         self.exp = ImageExporterCustom(self)
  709.         self.exp.export()
  710.  
  711.     def getMenu(self, event):
  712.         if self.menu is None:
  713.             self.menu = QMenuCustom()
  714.             self.viewAll = QtGui.QAction("View All", self.menu)
  715.             self.exportImage = QtGui.QAction("Export image", self.menu)
  716.             self.viewAll.triggered[()].connect(self.autoRange)
  717.             self.exportImage.triggered[()].connect(self.export)
  718.             self.menu.addAction(self.viewAll)
  719.             self.menu.addAction(self.exportImage)
  720.         return self.menu
  721.  
  722.  
  723. class ViewMode():
  724.     def __init__(self, id, cmap):
  725.         self.id = id
  726.         self.cmap = cmap
  727.         self.getLookupTable()
  728.  
  729.     def getLookupTable(self):
  730.         lut = [[int(255 * val) for val in self.cmap(i)[:3]] for i in xrange(256)]
  731.         lut = np.array(lut, dtype=np.ubyte)
  732.         self.lut = lut
  733.  
  734.  
  735. class MultiRoiViewBox(pg.ViewBox):
  736.     sigROIchanged = QtCore.Signal(object)
  737.     clicked = QtCore.pyqtSignal(float, float)
  738.     hovering = QtCore.pyqtSignal(float, float)
  739.     roi_placed = QtCore.pyqtSignal(PolyLineROIcustom)
  740.  
  741.     def __init__(self, parent=None, border=None, lockAspect=False,
  742.                  enableMouse=True, invertY=False, enableMenu=True, name=None):
  743.         pg.ViewBox.__init__(self, parent, border, lockAspect,
  744.                             enableMouse, invertY, enableMenu, name)
  745.  
  746.         self.crosshair_visible = True
  747.         self.rois = []
  748.         self.currentROIindex = None
  749.         self.img = None
  750.         self.menu = None  # Override pyqtgraph ViewBoxMenu
  751.         self.menu = self.getMenu(None)
  752.         self.NORMAL = ViewMode(0, plt.cm.gray)
  753.         self.DEXA = ViewMode(1, plt.cm.jet)
  754.         self.viewMode = self.NORMAL
  755.         self.drawROImode = False
  756.         self.drawingROI = None
  757.         self.vLine = pg.InfiniteLine(angle=90, movable=False)
  758.         self.hLine = pg.InfiniteLine(angle=0, movable=False)
  759.  
  760.         self.mouseclickeventCount = 0
  761.  
  762.         l = QtGui.QGraphicsGridLayout()
  763.         l.setHorizontalSpacing(0)
  764.         l.setVerticalSpacing(0)
  765.         xScale = pg.AxisItem(orientation='bottom', linkView=self)
  766.         l.addItem(xScale, 1, 1)
  767.         yScale = pg.AxisItem(orientation='left', linkView=self)
  768.         l.addItem(yScale, 0, 0)
  769.         xScale.setLabel(text="<span style='color: #ff0000; font-weight: bold'>X</span> <i>Axis</i>", units="s")
  770.         yScale.setLabel('Y Axis', units='V')
  771.  
  772.     def getContextMenus(self, ev):
  773.         return None
  774.  
  775.     def raiseContextMenu(self, ev):
  776.         if not self.menuEnabled(): return
  777.         menu = self.getMenu(ev)
  778.         pos = ev.screenPos()
  779.         menu.popup(QtCore.QPoint(pos.x(), pos.y()))
  780.  
  781.     def export(self):
  782.         self.exp = ImageExporterCustom(self)
  783.         self.exp.export()
  784.  
  785.     def mouseClickEvent(self, ev):
  786.         if self.drawROImode:
  787.             ev.accept()
  788.             # print('mouseClickEvent->drawPolygonRoi')
  789.             self.drawPolygonRoi(ev)
  790.         elif ev.button() == QtCore.Qt.RightButton and self.menuEnabled():
  791.             ev.accept()
  792.             self.raiseContextMenu(ev)
  793.         elif ev.button() == QtCore.Qt.LeftButton:
  794.             ev.accept()
  795.             pos = self.mapToItem(self.img, ev.pos())
  796.             self.clicked.emit(pos.x(), pos.y())
  797.             # if self.mouseclickeventCount <= 1:
  798.             #     proxy = pg.SignalProxy(self.scene().sigMouseMoved, rateLimit=60, slot=self.mouseMoved)
  799.             #     self.scene().sigMouseMoved.connect(self.mouseMoved)
  800.  
  801.     def mouseMoved(self, ev):
  802.         # pos = ev ## using signal proxy turns original arguments into a tuple (or not...)
  803.         if self.sceneBoundingRect().contains(ev) and not self.drawROImode:
  804.             mousePoint = self.mapSceneToView(ev)
  805.             index = int(mousePoint.x())
  806.             # if index > 0 and index < len(data1):
  807.             #    label.setText("<span style='font-size: 12pt'>x=%0.1f,   <span style='color: red'>y1=%0.1f</span>,   <span style='color: green'>y2=%0.1f</span>" % (mousePoint.x(), data1[index], data2[index]))
  808.             self.vLine.setPos(mousePoint.x())
  809.             self.hLine.setPos(mousePoint.y())
  810.  
  811.             # pos = self.mapToItem(self.img, ev.pos())
  812.         mousePoint = self.mapSceneToView(ev)
  813.         # print(str(mousePoint.x())+","+str(mousePoint.y()))
  814.         self.hovering.emit(mousePoint.x(), mousePoint.y())
  815.  
  816.     def addPolyRoiRequest(self):
  817.         """Function to add a Polygon ROI"""
  818.         # print('addPolyRoiRequest')
  819.         self.drawROImode = True
  820.         for roi in self.rois:
  821.             roi.setActive(False)
  822.  
  823.     def endPolyRoiRequest(self):
  824.         self.drawROImode = False  # Deactivate drawing mode
  825.         self.drawingROI = None  # No roi being drawn, so set to None
  826.         for r in self.rois:
  827.             r.setActive(True)
  828.  
  829.     def addPolyLineROI(self, handlePositions, name=None):
  830.         roi = PolyLineROIcustom(handlePositions=handlePositions, removable=True)
  831.         roi.setName(name)
  832.         self.addItem(roi)  # Add roi to viewbox
  833.         self.rois.append(roi)  # Add to list of rois
  834.         self.selectROI(roi)
  835.         self.setCurrentROIindex(roi)
  836.         roi.translatable = True
  837.         # roi.setAcceptedMouseButtons(QtCore.Qt.LeftButton or QtCore.Qt.RightButton)
  838.         roi.setActive(True)
  839.         for seg in roi.segments:
  840.             seg.setSelectable(True)
  841.         for h in roi.handles:
  842.             h['item'].setSelectable(True)
  843.         # Setup signals
  844.         roi.sigClicked.connect(self.selectROI)
  845.         roi.sigRegionChanged.connect(self.roiChanged)
  846.         roi.sigRemoveRequested.connect(self.removeROI)
  847.         roi.sigCopyRequested.connect(self.copyROI)
  848.         roi.sigSaveRequested.connect(self.saveROI)
  849.         return roi
  850.  
  851.     def drawPolygonRoi(self, ev):
  852.         "Function to draw a polygon ROI"
  853.         roi = self.drawingROI
  854.         pos = self.mapSceneToView(ev.scenePos())
  855.  
  856.         if ev.button() == QtCore.Qt.LeftButton:
  857.             if roi is None:
  858.                 roi = PolyLineROIcustom(removable=False)
  859.                 # roi.setName('ROI-%i'% self.getROIid()) # Do this before self.selectROIs(roi)
  860.                 self.drawingROI = roi
  861.                 roi.addFreeHandle(pos)
  862.                 roi.addFreeHandle(pos)
  863.                 self.addItem(roi)  # Add roi to viewbox
  864.                 self.rois.append(roi)  # Add to list of rois
  865.                 self.selectROI(roi)
  866.                 self.sortROIs()
  867.                 self.setCurrentROIindex(roi)
  868.                 roi.translatable = False
  869.                 h = roi.handles[-1]['item']
  870.                 h.scene().sigMouseMoved.connect(h.movePoint)
  871.             else:
  872.                 h = roi.handles[-1]['item']
  873.                 h.scene().sigMouseMoved.disconnect()
  874.                 roi.addFreeHandle(pos)
  875.                 h = roi.handles[-1]['item']
  876.                 h.scene().sigMouseMoved.connect(h.movePoint)
  877.                 # Add a segment between the handles
  878.             roi.addSegment(roi.handles[-2]['item'], roi.handles[-1]['item'])
  879.             # Set segment and handles to non-selectable
  880.             seg = roi.segments[-1]
  881.             seg.setSelectable(False)
  882.             for h in seg.handles:
  883.                 h['item'].setSelectable(False)
  884.  
  885.         elif (ev.button() == QtCore.Qt.MiddleButton) or \
  886.                 (ev.button() == QtCore.Qt.RightButton and (roi == None or len(roi.segments) < 3)):
  887.             if roi != None:
  888.                 # Remove handle and disconnect from scene
  889.                 h = roi.handles[-1]['item']
  890.                 h.scene().sigMouseMoved.disconnect()
  891.                 roi.removeHandle(h)
  892.                 # Removed roi from viewbox
  893.                 self.removeItem(roi)
  894.                 self.rois.pop(self.currentROIindex)
  895.                 self.setCurrentROIindex(None)
  896.             # Exit ROI drawing mode
  897.             self.endPolyRoiRequest()
  898.  
  899.         elif ev.button() == QtCore.Qt.RightButton:
  900.             # Remove last handle
  901.             h = roi.handles[-1]['item']
  902.             h.scene().sigMouseMoved.disconnect()
  903.             roi.removeHandle(h)
  904.             # Add segment to close ROI
  905.             roi.addSegment(roi.handles[-1]['item'], roi.handles[0]['item'])
  906.             # Setup signals
  907.             roi.sigClicked.connect(self.selectROI)
  908.             roi.sigRegionChanged.connect(self.roiChanged)
  909.             roi.sigRemoveRequested.connect(self.removeROI)
  910.             roi.sigCopyRequested.connect(self.copyROI)
  911.             roi.sigSaveRequested.connect(self.saveROI)
  912.             # Re-activate mouse clicks for all roi, segments and handles
  913.             roi.removable = True
  914.             roi.translatable = True
  915.             for seg in roi.segments:
  916.                 seg.setSelectable(True)
  917.             for h in roi.handles:
  918.                 h['item'].setSelectable(True)
  919.             # Exit ROI drawing mode
  920.             self.endPolyRoiRequest()
  921.             self.roi_placed.emit(roi)
  922.  
  923.     def autoDrawPolygonRoi(self, name, pos=QtCore.QPointF(0, 0), finished=False):
  924.         "Function to draw a polygon ROI"
  925.         roi = self.drawingROI
  926.  
  927.         if not finished:
  928.             if roi is None:
  929.                 roi = PolyLineROIcustom(removable=False)
  930.                 roi.setName(name)  # Do this before self.selectROIs(roi)
  931.                 self.drawingROI = roi
  932.                 roi.addFreeHandle(pos)
  933.                 roi.addFreeHandle(pos)
  934.                 self.addItem(roi)  # Add roi to viewbox
  935.                 self.rois.append(roi)  # Add to list of rois
  936.                 self.selectROI(roi)
  937.                 self.sortROIs()
  938.                 self.setCurrentROIindex(roi)
  939.                 roi.translatable = False
  940.                 h = roi.handles[-1]['item']
  941.                 h.scene().sigMouseMoved.connect(h.movePoint)
  942.             else:
  943.                 h = roi.handles[-1]['item']
  944.                 h.scene().sigMouseMoved.disconnect()
  945.                 roi.addFreeHandle(pos)
  946.                 h = roi.handles[-1]['item']
  947.                 h.scene().sigMouseMoved.connect(h.movePoint)
  948.                 # Add a segment between the handles
  949.             roi.addSegment(roi.handles[-2]['item'], roi.handles[-1]['item'])
  950.             # Set segment and handles to non-selectable
  951.             seg = roi.segments[-1]
  952.             seg.setSelectable(False)
  953.             for h in seg.handles:
  954.                 h['item'].setSelectable(False)
  955.  
  956.         elif finished:
  957.             # Remove last handle
  958.             h = roi.handles[-1]['item']
  959.             h.scene().sigMouseMoved.disconnect()
  960.             roi.removeHandle(h)
  961.             # Add segment to close ROI
  962.             roi.addSegment(roi.handles[-1]['item'], roi.handles[0]['item'])
  963.             # Setup signals
  964.             roi.sigClicked.connect(self.selectROI)
  965.             roi.sigRegionChanged.connect(self.roiChanged)
  966.             roi.sigRemoveRequested.connect(self.removeROI)
  967.             roi.sigCopyRequested.connect(self.copyROI)
  968.             roi.sigSaveRequested.connect(self.saveROI)
  969.             # Re-activate mouse clicks for all roi, segments and handles
  970.             roi.removable = True
  971.             roi.translatable = True
  972.             for seg in roi.segments:
  973.                 seg.setSelectable(True)
  974.             for h in roi.handles:
  975.                 h['item'].setSelectable(True)
  976.             # Exit ROI drawing mode
  977.             self.endPolyRoiRequest()
  978.  
  979.     def getMenu(self, event):
  980.         if self.menu is None:
  981.             self.menu = QtGui.QMenu()
  982.             # Submenu to add ROIs
  983.             self.submenu = QtGui.QMenu("Add ROI", self.menu)
  984.             # self.addROIRectAct = QActionCustom("Rectangular",  self.submenu)
  985.             self.addROIRectAct = QtGui.QAction("Rectangular", self.submenu)
  986.             # self.addROIPolyAct = QActionCustom("Polygon",  self.submenu)
  987.             self.addROIPolyAct = QtGui.QAction("Polygon", self.submenu)
  988.             # self.addROIRectAct.clickEvent.connect(self.addRoiRequest)
  989.             self.addROIRectAct.triggered.connect(self.addROI)
  990.             # self.addROIPolyAct.clickEvent.connect(self.addPolyRoiRequest)
  991.             self.addROIPolyAct.triggered.connect(self.addPolyRoiRequest)
  992.             self.submenu.addAction(self.addROIRectAct)
  993.             self.submenu.addAction(self.addROIPolyAct)
  994.  
  995.             # self.loadROIAct  = QActionCustom("Load ROI", self.menu)
  996.             self.loadROIAct = QtGui.QAction("Load ROI", self.menu)
  997.             self.dexaMode = QtGui.QAction("DEXA mode", self.menu)
  998.             self.viewAll = QtGui.QAction("View All", self.menu)
  999.             self.exportImage = QtGui.QAction("Export image", self.menu)
  1000.  
  1001.             # self.loadROIAct.clickEvent.connect(self.loadROI)
  1002.             self.loadROIAct.triggered[()].connect(self.loadROI)
  1003.             self.dexaMode.toggled.connect(self.toggleViewMode)
  1004.             self.viewAll.triggered[()].connect(self.autoRange)
  1005.             self.exportImage.triggered[()].connect(self.export)
  1006.  
  1007.             self.menu.addAction(self.viewAll)
  1008.             self.menu.addAction(self.dexaMode)
  1009.             self.menu.addAction(self.exportImage)
  1010.             self.menu.addSeparator()
  1011.             self.menu.addMenu(self.submenu)
  1012.             self.menu.addAction(self.loadROIAct)
  1013.             self.dexaMode.setCheckable(True)
  1014.             # Update action event. This enables passing of the event to the fuction connected to the
  1015.         # action i.e.  event will be passed to self.addRoiRequest when a Rectangular ROI is clicked
  1016.         # self.addROIRectAct.updateEvent(event)
  1017.         # self.addROIPolyAct.updateEvent(event)
  1018.         return self.menu
  1019.  
  1020.     def getMenu(self, event):
  1021.         if self.menu is None:
  1022.             self.menu = QtGui.QMenu()
  1023.             # Submenu to add ROIs
  1024.             self.submenu = QtGui.QMenu("Add ROI", self.menu)
  1025.             # self.addROIRectAct = QActionCustom("Rectangular",  self.submenu)
  1026.             self.addROIRectAct = QtGui.QAction("Rectangular", self.submenu)
  1027.             # self.addROIPolyAct = QActionCustom("Polygon",  self.submenu)
  1028.             self.addROIPolyAct = QtGui.QAction("Polygon", self.submenu)
  1029.             # self.addROIRectAct.clickEvent.connect(self.addRoiRequest)
  1030.             self.addROIRectAct.triggered.connect(self.addROI)
  1031.             # self.addROIPolyAct.clickEvent.connect(self.addPolyRoiRequest)
  1032.             self.addROIPolyAct.triggered.connect(self.addPolyRoiRequest)
  1033.             self.submenu.addAction(self.addROIRectAct)
  1034.             self.submenu.addAction(self.addROIPolyAct)
  1035.  
  1036.             # self.loadROIAct  = QActionCustom("Load ROI", self.menu)
  1037.             self.loadROIAct = QtGui.QAction("Load ROI", self.menu)
  1038.             self.dexaMode = QtGui.QAction("DEXA mode", self.menu)
  1039.             self.viewAll = QtGui.QAction("View All", self.menu)
  1040.             self.exportImage = QtGui.QAction("Export image", self.menu)
  1041.  
  1042.             # self.loadROIAct.clickEvent.connect(self.loadROI)
  1043.             self.loadROIAct.triggered[()].connect(self.loadROI)
  1044.             self.dexaMode.toggled.connect(self.toggleViewMode)
  1045.             self.viewAll.triggered[()].connect(self.autoRange)
  1046.             self.exportImage.triggered[()].connect(self.export)
  1047.  
  1048.             self.menu.addAction(self.viewAll)
  1049.             self.menu.addAction(self.dexaMode)
  1050.             self.menu.addAction(self.exportImage)
  1051.             self.menu.addSeparator()
  1052.             self.menu.addMenu(self.submenu)
  1053.             self.menu.addAction(self.loadROIAct)
  1054.             self.dexaMode.setCheckable(True)
  1055.             # Update action event. This enables passing of the event to the fuction connected to the
  1056.         # action i.e.  event will be passed to self.addRoiRequest when a Rectangular ROI is clicked
  1057.         # self.addROIRectAct.updateEvent(event)
  1058.         # self.addROIPolyAct.updateEvent(event)
  1059.         return self.menu
  1060.  
  1061.     def setCurrentROIindex(self, roi=None):
  1062.         """ Use this function to change currentROIindex value to ensure a signal is emitted"""
  1063.         if roi == None:
  1064.             self.currentROIindex = None
  1065.         else:
  1066.             self.currentROIindex = self.rois.index(roi)
  1067.         self.sigROIchanged.emit(roi)
  1068.  
  1069.     def roiChanged(self, roi):
  1070.         self.sigROIchanged.emit(roi)
  1071.  
  1072.     def getCurrentROIindex(self):
  1073.         return self.currentROIindex
  1074.  
  1075.     def selectROI(self, roi):
  1076.         """ Selection control of ROIs """
  1077.         # If no ROI is currently selected (currentROIindex is None), select roi
  1078.         if self.currentROIindex == None:
  1079.             roi.setSelected(True)
  1080.             self.setCurrentROIindex(roi)
  1081.         # If an ROI is already selected...
  1082.         else:
  1083.             roiSelected = self.rois[self.currentROIindex]
  1084.             roiSelected.setSelected(False)
  1085.             # If a different roi is already selected, then select roi
  1086.             if self.currentROIindex != self.rois.index(roi):
  1087.                 self.setCurrentROIindex(roi)
  1088.                 roi.setSelected(True)
  1089.             # If roi is already selected, then unselect
  1090.             else:
  1091.                 self.setCurrentROIindex(None)
  1092.  
  1093.     def addRoiRequest(self, ev):
  1094.         """ Function to addROI at an event screen position """
  1095.         # Get position
  1096.         pos = self.mapSceneToView(ev.scenePos())
  1097.         xpos = pos.x()
  1098.         ypos = pos.y()
  1099.         # Shift down by size
  1100.         xr, yr = self.viewRange()
  1101.         xsize = 0.25 * (xr[1] - xr[0])
  1102.         ysize = 0.25 * (yr[1] - yr[0])
  1103.         xysize = min(xsize, ysize)
  1104.         if xysize == 0: xysize = 100
  1105.         ypos -= xysize
  1106.         # Create ROI
  1107.         xypos = (xpos, ypos)
  1108.         self.addROI(pos=xypos)
  1109.  
  1110.     def addROI(self, name, pos=None, size=None, angle=0.0):
  1111.         """ Add an ROI to the ViewBox """
  1112.         if name is None:
  1113.             raise ValueError('ROIs must have names. A nameless ROI was loaded')
  1114.         xr, yr = self.viewRange()
  1115.         if pos is None:
  1116.             posx = xr[0] + 0.05 * (xr[1] - xr[0])
  1117.             posy = yr[0] + 0.05 * (yr[1] - yr[0])
  1118.             pos = [posx, posy]
  1119.         if size is None:
  1120.             xsize = 0.25 * (xr[1] - xr[0])
  1121.             ysize = 0.25 * (yr[1] - yr[0])
  1122.             xysize = min(xsize, ysize)
  1123.             if xysize == 0: xysize = 100
  1124.             size = [xysize, xysize]
  1125.         roi = RectROIcustom(pos, size, angle, removable=True, pen=(255, 0, 0))
  1126.         roi.setName(name)
  1127.         # Setup signals
  1128.         # roi.setName('ROI-%i' % self.getROIid())
  1129.         roi.sigClicked.connect(self.selectROI)
  1130.         roi.sigRegionChanged.connect(self.roiChanged)
  1131.         roi.sigRemoveRequested.connect(self.removeROI)
  1132.         roi.sigCopyRequested.connect(self.copyROI)
  1133.         roi.sigSaveRequested.connect(self.saveROI)
  1134.         # Keep track of rois
  1135.         self.addItem(roi)
  1136.         self.rois.append(roi)
  1137.         self.selectROI(roi)
  1138.         self.sortROIs()
  1139.         self.setCurrentROIindex(roi)
  1140.         return roi
  1141.  
  1142.     def sortROIs(self):
  1143.         """ Sort self.rois by roi name and adjust self.currentROIindex as necessary """
  1144.         if len(self.rois) == 0: return
  1145.         if self.currentROIindex == None:
  1146.             self.rois.sort()
  1147.         else:
  1148.             roiCurrent = self.rois[self.currentROIindex]
  1149.             # self.rois.sort()
  1150.             self.currentROIindex = self.rois.index(roiCurrent)
  1151.  
  1152.     def getROIid(self):
  1153.         """ Get available and unique number for ROI name """
  1154.         if not self.rois:
  1155.             return 1
  1156.         parseable_rois = [roi for roi in self.rois if '-' in roi.name]
  1157.         nums = [(roi.name.split('-')[-1]) for roi in parseable_rois if roi.name != None]
  1158.         nid = 1
  1159.         if len(nums) > 0:
  1160.             while (True):
  1161.                 if nid not in nums: break
  1162.                 nid += 1
  1163.         return nid
  1164.  
  1165.     def copyROI(self, offset=0.0):
  1166.         """ Copy current ROI. Offset from original for visibility """
  1167.         if self.currentROIindex != None:
  1168.             osFract = 0.05
  1169.             roi = self.rois[self.currentROIindex]
  1170.             # For rectangular ROI, offset by a fraction of the rotated size
  1171.             if type(roi) == RectROIcustom:
  1172.                 roiState = roi.getState()
  1173.                 pos = roiState['pos']
  1174.                 size = roiState['size']
  1175.                 angle = roiState['angle']
  1176.                 dx, dy = np.array(size) * osFract
  1177.                 ang = np.radians(angle)
  1178.                 cosa = np.cos(ang)
  1179.                 sina = np.sin(ang)
  1180.                 dxt = dx * cosa - dy * sina
  1181.                 dyt = dx * sina + dy * cosa
  1182.                 offset = QtCore.QPointF(dxt, dyt)
  1183.                 self.addROI(pos + offset, size, angle)
  1184.             # For a polyline ROI, offset by a fraction of the bounding rectangle
  1185.             if type(roi) == PolyLineROIcustom:
  1186.                 br = roi.shape().boundingRect()
  1187.                 size = np.array([br.width(), br.height()])
  1188.                 osx, osy = size * osFract
  1189.                 offset = QtCore.QPointF(osx, osy)
  1190.                 hps = [i[-1] for i in roi.getSceneHandlePositions(index=None)]
  1191.                 hpsOffset = [self.mapSceneToView(hp) + offset for hp in hps]
  1192.                 self.addPolyLineROI(hpsOffset)
  1193.  
  1194.     def saveROI(self, fileName=''):
  1195.         """ Save the highlighted ROI to file """
  1196.         if self.currentROIindex != None:
  1197.             roi = self.rois[self.currentROIindex]
  1198.             if not fileName:
  1199.                 fileName = QtGui.QFileDialog.getSaveFileName(None, self.tr("Save ROI"), QtCore.QDir.currentPath(),
  1200.                                                              self.tr("ROI (*.roi)"))
  1201.                 # Fix for PyQt/PySide compatibility. PyQt returns a QString, whereas PySide returns a tuple (first entry is filename as string)
  1202.                 if isinstance(fileName, types.TupleType): fileName = fileName[0]
  1203.                 if hasattr(QtCore, 'QString') and isinstance(fileName, QtCore.QString): fileName = str(fileName)
  1204.             if not fileName == '':
  1205.                 if type(roi) == RectROIcustom:
  1206.                     roiState = roi.saveState()
  1207.                     roiState['type'] = 'RectROIcustom'
  1208.                     roiState['name'] = roi.name
  1209.                 elif type(roi) == PolyLineROIcustom:
  1210.                     roiState = {}
  1211.                     hps = [self.mapSceneToView(i[-1]) for i in roi.getSceneHandlePositions(index=None)]
  1212.                     hps = [[hp.x(), hp.y()] for hp in hps]
  1213.                     roiState['type'] = 'PolyLineROIcustom'
  1214.                     roiState['handlePositions'] = hps
  1215.                     roiState['name'] = roi.name
  1216.                 pickle.dump(roiState, open(fileName, "wb"))
  1217.  
  1218.     def addRoi(self, roipath, roiname, roimode='static'):
  1219.         roistate = pickle.load(open(roipath, 'rb'))
  1220.         if roistate['type'] == 'RectROIcustom':
  1221.             roi = self.addROI(roistate['pos'], roistate['size'], roistate['angle'])
  1222.         elif roistate['type'] == 'PolyLineROIcustom':
  1223.             roi = self.addPolyLineROI(roistate['handlePositions'], roistate['name'])
  1224.         else:
  1225.             raise UnsupportedRoiTypeError()
  1226.         roi.setName(roiname)
  1227.         self.selectROI(roi)
  1228.  
  1229.     def getRoi(self, roiname):
  1230.         rois = [roi for roi in self.rois if roi.name == roiname]
  1231.         assert (len(rois) == 1)
  1232.         return rois[0]
  1233.  
  1234.     def removeRoi(self, roiname):
  1235.         rois = [roi for roi in self.rois if roi.name == roiname]
  1236.         if not rois:
  1237.             return
  1238.         assert (len(rois) == 1)
  1239.         roi = rois[0]
  1240.         self.rois.remove(roi)
  1241.         self.removeItem(roi)
  1242.  
  1243.     def loadROI(self, fileNames=None):
  1244.         """ Load a previously saved ROI from file """
  1245.         if fileNames == None:
  1246.             fileNames = QtGui.QFileDialog.getOpenFileNames(None, self.tr("Load ROI"),
  1247.                                                            QtCore.QDir.currentPath(),
  1248.                                                            self.tr("ROI (*.roi)"))
  1249.         if hasattr(QtCore, 'QStringList') and \
  1250.                 isinstance(fileNames, QtCore.QStringList): fileNames = [str(i) for i in fileNames]
  1251.         if len(fileNames) > 0:
  1252.             for fileName in fileNames:
  1253.                 if fileName != '':
  1254.                     roiState = pickle.load(open(fileName, "rb"))
  1255.                     if roiState['type'] == 'RectROIcustom':
  1256.                         self.addROI(roiState['name'], roiState['pos'], roiState['size'], roiState['angle'])
  1257.                     elif roiState['type'] == 'PolyLineROIcustom':
  1258.                         self.addPolyLineROI(roiState['handlePositions'], roiState['name'])
  1259.  
  1260.     def removeROI(self):
  1261.         """ Delete the highlighted ROI """
  1262.         if self.currentROIindex != None:
  1263.             roi = self.rois[self.currentROIindex]
  1264.             self.rois.pop(self.currentROIindex)
  1265.             self.removeItem(roi)
  1266.             self.setCurrentROIindex(None)
  1267.  
  1268.     def toggleViewMode(self, isChecked):
  1269.         """ Toggles between NORMAL (Black/White) and DEXA mode (colour) """
  1270.         if isChecked:
  1271.             viewMode = self.DEXA
  1272.         else:
  1273.             viewMode = self.NORMAL
  1274.         self.setViewMode(viewMode)
  1275.  
  1276.     def setViewMode(self, viewMode):
  1277.         self.viewMode = viewMode
  1278.         self.updateView()
  1279.  
  1280.     def updateView(self):
  1281.         self.background.setBrush(fn.mkBrush(self.viewMode.lut[0]))
  1282.         self.background.show()
  1283.         if self.img == None: return
  1284.         # todo: validate safe removal of line
  1285.         # else: self.img.setLookupTable(self.viewMode.lut)
  1286.  
  1287.     def update_rect(self, x1, y1, x2, y2):
  1288.         if not self.img:
  1289.             return
  1290.         self.img.setRect(QtCore.QRectF(x1, y1, x2, y2))
  1291.  
  1292.     def showImage(self, arr):
  1293.         arr = arr.astype("float64")
  1294.         if arr is None:
  1295.             self.img = None
  1296.             return
  1297.         if self.img == None:
  1298.             self.img = pg.ImageItem(arr, autoRange=False, autoLevels=False)
  1299.             self.addItem(self.img)
  1300.         # Add/readd crosshair
  1301.         if self.crosshair_visible:
  1302.             self.addItem(self.vLine, ignoreBounds=True)
  1303.             self.addItem(self.hLine, ignoreBounds=True)
  1304.         proxy = pg.SignalProxy(self.scene().sigMouseMoved, rateLimit=60, slot=self.mouseMoved)
  1305.         self.scene().sigMouseMoved.connect(self.mouseMoved)
  1306.         self.img.setImage(arr)
  1307.         self.updateView()
  1308.  
  1309. import pyqtgraph as pg
  1310. from pyqtgraph.Qt import QtCore, QtGui
  1311. from pyqtgraph.exporters.ImageExporter import ImageExporter
  1312. import numpy as np
  1313. # ------------------------- custom_items.py --------------------------- #
  1314.  
  1315. class QMenuCustom(QtGui.QMenu):
  1316.     """ Custum QMenu that closes on leaveEvent """
  1317.  
  1318.     def __init__(self, parent=None):
  1319.         QtGui.QMenu.__init__(self, parent)
  1320.  
  1321.     def leaveEvent(self, ev):
  1322.         self.hide()
  1323.  
  1324.  
  1325. class QActionCustom(QtGui.QAction):
  1326.     """ QAction class modified to emit a single argument (an event)"""
  1327.     clickEvent = QtCore.Signal(object)
  1328.  
  1329.     def __init__(self, name="", parent=None):
  1330.         QtGui.QAction.__init__(self, name, parent)
  1331.         self.triggered.connect(self.clicked)
  1332.         # self.event = None
  1333.  
  1334.     def updateEvent(self, event):
  1335.         self.event = event
  1336.  
  1337.     def clicked(self):
  1338.         self.clickEvent.emit(self.event)
  1339.  
  1340.  
  1341. class ImageExporterCustom(ImageExporter):
  1342.     """
  1343.    Subclass to change preferred image output to bmp. Currently there are some issues
  1344.    with png, as it creates some lines around the image
  1345.    """
  1346.  
  1347.     def __init__(self, item):
  1348.         ImageExporter.__init__(self, item)
  1349.  
  1350.     def export(self, fileName=None, toBytes=False, copy=False):
  1351.         if fileName is None and not toBytes and not copy:
  1352.             filter = ["*." + str(f) for f in QtGui.QImageWriter.supportedImageFormats()]
  1353.             preferred = ['*.bmp', '*.png', '*.tif', '*.jpg']
  1354.             for p in preferred[::-1]:
  1355.                 if p in filter:
  1356.                     filter.remove(p)
  1357.                     filter.insert(0, p)
  1358.             self.fileSaveDialog(filter=filter)
  1359.             return
  1360.  
  1361.         targetRect = QtCore.QRect(0, 0, self.params['width'], self.params['height'])
  1362.         sourceRect = self.getSourceRect()
  1363.  
  1364.         bg = np.empty((self.params['width'], self.params['height'], 4), dtype=np.ubyte)
  1365.         color = self.params['background']
  1366.         bg[:, :, 0] = color.blue()
  1367.         bg[:, :, 1] = color.green()
  1368.         bg[:, :, 2] = color.red()
  1369.         bg[:, :, 3] = color.alpha()
  1370.         self.png = pg.makeQImage(bg, alpha=True)
  1371.  
  1372.         origTargetRect = self.getTargetRect()
  1373.         resolutionScale = targetRect.width() / origTargetRect.width()
  1374.  
  1375.         painter = QtGui.QPainter(self.png)
  1376.         try:
  1377.             self.setExportMode(True, {'antialias': self.params['antialias'], 'background': self.params['background'],
  1378.                                       'painter': painter, 'resolutionScale': resolutionScale})
  1379.             painter.setRenderHint(QtGui.QPainter.Antialiasing, self.params['antialias'])
  1380.             self.getScene().render(painter, QtCore.QRectF(targetRect), QtCore.QRectF(sourceRect))
  1381.         finally:
  1382.             self.setExportMode(False)
  1383.         painter.end()
  1384.  
  1385.         if copy:
  1386.             QtGui.QApplication.clipboard().setImage(self.png)
  1387.         elif toBytes:
  1388.             return self.png
  1389.         else:
  1390.             self.png.save(fileName)
  1391.  
  1392. import os
  1393. import sys
  1394. import numpy as np
  1395. import pyqtgraph as pg
  1396. from PyQt4.QtGui import *
  1397. from PyQt4.QtCore import *
  1398. from PyQt4 import QtCore
  1399. from PyQt4 import QtGui
  1400. import uuid
  1401. import csv
  1402. # ------------------------ main.py ----------------------- #
  1403.  
  1404. class RoiItemModel(QAbstractListModel):
  1405.     textChanged = pyqtSignal(str, str)
  1406.  
  1407.     def __init__(self, parent=None):
  1408.         super(RoiItemModel, self).__init__(parent)
  1409.         self.rois = []
  1410.  
  1411.     def appendRoi(self, name):
  1412.         self.rois.append(name)
  1413.         row = len(self.rois) - 1
  1414.         self.dataChanged.emit(self.index(row), self.index(row))
  1415.  
  1416.     def rowCount(self, parent):
  1417.         return len(self.rois)
  1418.  
  1419.     def data(self, index, role):
  1420.         if role == Qt.DisplayRole:
  1421.             return self.rois[index.row()]
  1422.         return
  1423.  
  1424.     def setData(self, index, value, role):
  1425.         if role in [Qt.DisplayRole, Qt.EditRole]:
  1426.             self.textChanged.emit(self.rois[index.row()], value)
  1427.             self.rois[index.row()] = str(value)
  1428.             return True
  1429.         return super(RoiItemModel, self).setData(index, value, role)
  1430.  
  1431.     def flags(self, index):
  1432.         return Qt.ItemIsSelectable | Qt.ItemIsEditable | Qt.ItemIsEnabled
  1433.  
  1434.     def removeRow(self, roi_to_remove):
  1435.         for roi in self.rois:
  1436.             if roi == roi_to_remove:
  1437.                 del roi
  1438.                 break
  1439.  
  1440.  
  1441. class Widget(QWidget):
  1442.     def __init__(self, project, parent=None):
  1443.         super(Widget, self).__init__(parent)
  1444.  
  1445.         if not project:
  1446.             return
  1447.         if project == "standalone":
  1448.             self.project = None
  1449.         else:
  1450.             self.project = project
  1451.  
  1452.         anatomy_rois = {"M1": [3, (1.0, 2.5)], "M2": [3, (1.5, 1.75)],
  1453.                         "AC": [3, (0.5, 0.0)], "HL": [3, (2.0, 0.0)],
  1454.                         "BC": [3, (3.5, -1.0)], "RS": [3, (0.5, -2.5)], "V1": [3, (2.5, -2.5)]}
  1455.         roi_names = list(anatomy_rois.keys())
  1456.         roi_sizes = [anatomy_rois[x][0] for x in anatomy_rois.keys()]
  1457.         roi_coord_x = [anatomy_rois[x][1][0] for x in anatomy_rois.keys()]
  1458.         roi_coord_y = [anatomy_rois[x][1][1] for x in anatomy_rois.keys()]
  1459.         self.headers = ["1) ROI Name", "2) Length", "3) X Coordinate", "4) Y Coordinate"]
  1460.         self.data = {self.headers[0]: roi_names, self.headers[1]: roi_sizes,
  1461.                      self.headers[2]: roi_coord_x, self.headers[3]: roi_coord_y}
  1462.  
  1463.         self.open_dialogs = []
  1464.         self.setup_ui()
  1465.         self.listview.setModel(QStandardItemModel())
  1466.  
  1467.         for f in ['f1', 'f2', 'f3']:
  1468.             self.listview.model().appendRow(QStandardItem(str(f)))
  1469.         self.listview.setCurrentIndex(self.listview.model().index(0, 0))
  1470.  
  1471.         model = RoiItemModel()
  1472.         model.textChanged.connect(self.update_project_roi)
  1473.         self.roi_list.setModel(model)
  1474.         self.roi_list.setSelectionMode(QAbstractItemView.ExtendedSelection)
  1475.         # A flag to see whether selected_roi_changed is being entered for the first time
  1476.         self.selected_roi_changed_flag = 0
  1477.         self.roi_list.selectionModel().selectionChanged[QItemSelection,
  1478.                                                         QItemSelection].connect(self.selected_roi_changed)
  1479.  
  1480.     def show_table(self):
  1481.         locs = zip(self.data[self.headers[0]], self.data[self.headers[1]],
  1482.                    self.data[self.headers[2]], self.data[self.headers[3]])
  1483.         model = AutoROICoords(self.data, len(list(locs)), 4)
  1484.         model.itemChanged.connect(self.update_auto_rois)
  1485.         model.show()
  1486.         self.open_dialogs.append(model)
  1487.  
  1488.     def roi_item_edited(self, item):
  1489.         new_name = item.text()
  1490.         prev_name = item.data(Qt.UserRole)
  1491.         # disconnect and reconnect signal
  1492.         self.roi_list.itemChanged.disconnect()
  1493.         item.setData(new_name, Qt.UserRole)
  1494.         self.roi_list.model().itemChanged[QStandardItem.setData].connect(self.roi_item_edited)
  1495.  
  1496.     def setup_ui(self):
  1497.         hbox = QHBoxLayout()
  1498.  
  1499.         self.view = MyGraphicsView(self.project)
  1500.         hbox.addWidget(self.view)
  1501.  
  1502.         vbox = QVBoxLayout()
  1503.         vbox.addWidget(QLabel('Choose video:'))
  1504.         self.listview = QListView()
  1505.         self.listview.setStyleSheet('QListView::item { height: 26px; }')
  1506.         vbox.addWidget(self.listview)
  1507.  
  1508.         vbox.addWidget(QLabel('ROI size NxN'))
  1509.         self.roi_size = QSpinBox()
  1510.         self.roi_size.setMinimum(1)
  1511.         self.roi_size.setValue(3)
  1512.         vbox.addWidget(self.roi_size)
  1513.         pb = QPushButton('auto ROI')
  1514.         pb.clicked.connect(self.auto_ROI)
  1515.         vbox.addWidget(pb)
  1516.         pb = QPushButton('auto ROI table')
  1517.         pb.clicked.connect(self.show_table)
  1518.         vbox.addWidget(pb)
  1519.         pb = QPushButton('Load anatomical coordinates (relative to selected origin)')
  1520.         pb.clicked.connect(self.load_ROI_table)
  1521.         vbox.addWidget(pb)
  1522.  
  1523.         vbox2 = QVBoxLayout()
  1524.         w = QWidget()
  1525.         w.setLayout(vbox2)
  1526.         vbox.addWidget(w)
  1527.  
  1528.         vbox.addWidget(QLabel('ROIs'))
  1529.         self.roi_list = QListView()
  1530.         vbox.addWidget(self.roi_list)
  1531.  
  1532.         hbox.addLayout(vbox)
  1533.         hbox.setStretch(0, 1)
  1534.         hbox.setStretch(1, 0)
  1535.         self.setLayout(hbox)
  1536.  
  1537.     def remove_all_rois(self):
  1538.         rois = self.view.vb.rois[:]
  1539.         for roi in rois:
  1540.             if not roi.isSelected:
  1541.                 self.view.vb.selectROI(roi)
  1542.             self.view.vb.removeROI()
  1543.  
  1544.     def selected_roi_changed(self, selection):
  1545.         if self.selected_roi_changed_flag == 0:
  1546.             self.selected_roi_changed_flag = self.selected_roi_changed_flag + 1
  1547.             return
  1548.         if not selection.indexes() or self.view.vb.drawROImode:
  1549.             return
  1550.  
  1551.         self.remove_all_rois()
  1552.  
  1553.         rois_selected = [str(self.roi_list.selectionModel().selectedIndexes()[x].data(Qt.DisplayRole))
  1554.                          for x in range(len(self.roi_list.selectionModel().selectedIndexes()))]
  1555.         rois_in_view = [self.view.vb.rois[x].name for x in range(len(self.view.vb.rois))]
  1556.         rois_to_add = [x for x in rois_selected if x not in rois_in_view]
  1557.         for roi_to_add in rois_to_add:
  1558.             self.view.vb.loadROI([os.getcwd() + '/' + roi_to_add + '.roi'])
  1559.  
  1560.     def update_project_roi(self, roi):
  1561.         name = roi.name
  1562.         if not name:
  1563.             raise ValueError('ROI has no name')
  1564.         if self.view.vb.drawROImode:
  1565.             return
  1566.  
  1567.         path = os.path.join(os.getcwd(), name + '.roi')
  1568.         self.view.vb.saveROI(path)
  1569.         self.roi_list.model().appendRoi(name)
  1570.  
  1571.     def load_ROI_table(self):
  1572.         text_file = QFileDialog.getOpenFileName(
  1573.             self, 'Load images', QSettings().value('last_load_text_path'),
  1574.             'Video files (*.csv *.txt)')
  1575.         if not text_file:
  1576.             return
  1577.         QSettings().setValue('last_load_text_path', os.path.dirname(text_file))
  1578.  
  1579.         roi_table = []  # np.empty(shape=(4, ))
  1580.         with open(text_file, 'rt', encoding='ascii') as csvfile:
  1581.             roi_table_it = csv.reader(csvfile, delimiter=',')
  1582.             for row in roi_table_it:
  1583.                 roi_table = roi_table + [row]
  1584.         roi_table = np.array(roi_table)
  1585.         self.headers = [str.strip(x) for x in roi_table[0,]]
  1586.         roi_table_range = range(len(roi_table))[1:]
  1587.         roi_names = [roi_table[x, 0] for x in roi_table_range]
  1588.         roi_sizes = [int(roi_table[x, 1]) for x in roi_table_range]
  1589.         roi_coord_x = [float(roi_table[x, 2]) for x in roi_table_range]
  1590.         roi_coord_y = [float(roi_table[x, 3]) for x in roi_table_range]
  1591.         self.data = {self.headers[0]: roi_names, self.headers[1]: roi_sizes,
  1592.                      self.headers[2]: roi_coord_x, self.headers[3]: roi_coord_y}
  1593.         self.auto_ROI()
  1594.  
  1595.     def update_auto_rois(self, item):
  1596.         col = item.column()
  1597.         row = item.row()
  1598.         try:
  1599.             val = float(item.text())
  1600.         except:
  1601.             val = str(item.text())
  1602.         header = item.tableWidget().horizontalHeaderItem(col).text()
  1603.         header = str(header)
  1604.         col_to_change = self.data[header]
  1605.         col_to_change[row] = val
  1606.         self.data[header] = col_to_change
  1607.         self.auto_ROI()
  1608.  
  1609.     def auto_ROI(self):
  1610.         locs = zip(self.data[self.headers[0]], self.data[self.headers[1]],
  1611.                    self.data[self.headers[2]], self.data[self.headers[3]])
  1612.  
  1613.         # Warning: size must always be the second column
  1614.         for quad in list(locs):
  1615.             half_length = quad[1]
  1616.             self.remove_all_rois()
  1617.             x1 = (quad[2] - half_length)
  1618.             x2 = (quad[2] - half_length)
  1619.             x3 = (quad[2] + half_length)
  1620.             x4 = (quad[2] + half_length)
  1621.             y1 = (quad[3] - half_length)
  1622.             y2 = (quad[3] + half_length)
  1623.             y3 = (quad[3] + half_length)
  1624.             y4 = (quad[3] - half_length)
  1625.  
  1626.             self.view.vb.addPolyRoiRequest()
  1627.             self.view.vb.autoDrawPolygonRoi(quad[0], pos=QtCore.QPointF(x1, y1))
  1628.             self.view.vb.autoDrawPolygonRoi(quad[0], pos=QtCore.QPointF(x2, y2))
  1629.             self.view.vb.autoDrawPolygonRoi(quad[0], pos=QtCore.QPointF(x3, y3))
  1630.             self.view.vb.autoDrawPolygonRoi(quad[0], pos=QtCore.QPointF(x4, y4))
  1631.             self.view.vb.autoDrawPolygonRoi(quad[0], pos=QtCore.QPointF(x4, y4))
  1632.             self.view.vb.autoDrawPolygonRoi(quad[0], finished=True)
  1633.             roi = self.view.vb.rois[0]
  1634.             self.update_project_roi(roi)
  1635.  
  1636.  
  1637. class AutoROICoords(QTableWidget):
  1638.     def __init__(self, data, *args):
  1639.         QTableWidget.__init__(self, *args)
  1640.         self.data = data
  1641.         self.resizeColumnsToContents()
  1642.         self.resizeRowsToContents()
  1643.         self.horizontalHeader().setResizeMode(QHeaderView.Stretch)
  1644.         self.verticalHeader().setResizeMode(QHeaderView.Stretch)
  1645.         self.setmydata()
  1646.  
  1647.     def setmydata(self):
  1648.         horHeaders = self.data.keys()
  1649.         for n, key in enumerate(sorted(horHeaders)):
  1650.             for m, item in enumerate(self.data[key]):
  1651.                 newitem = QTableWidgetItem(str(item))
  1652.                 self.setItem(m, n, newitem)
  1653.         self.setHorizontalHeaderLabels(sorted(horHeaders))
  1654.  
  1655.  
  1656. class MyPlugin:
  1657.     def __init__(self, project):
  1658.         self.name = 'Auto ROI placer'
  1659.         self.widget = Widget(project)
  1660.  
  1661.     def run(self):
  1662.         pass
  1663.  
  1664. if __name__ == '__main__':
  1665.     app = QApplication(sys.argv)
  1666.     app.aboutToQuit.connect(app.deleteLater)
  1667.     w = QMainWindow()
  1668.     w.setCentralWidget(Widget("standalone"))
  1669.     w.show()
  1670.     app.exec_()
  1671.     sys.exit()
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement