GraphicsLayout.py 5.74 KB
Newer Older
1 2
from pyqtgraph.Qt import QtGui, QtCore
import pyqtgraph.functions as fn
3
from .GraphicsWidget import GraphicsWidget
Luke Campagnola's avatar
Luke Campagnola committed
4 5 6 7
## Must be imported at the end to avoid cyclic-dependency hell:
from .ViewBox import ViewBox
from .PlotItem import PlotItem
from .LabelItem import LabelItem
8 9 10 11 12

__all__ = ['GraphicsLayout']
class GraphicsLayout(GraphicsWidget):
    """
    Used for laying out GraphicsWidgets in a grid.
Luke Campagnola's avatar
Luke Campagnola committed
13
    This is usually created automatically as part of a :class:`GraphicsWindow <pyqtgraph.GraphicsWindow>` or :class:`GraphicsLayoutWidget <pyqtgraph.GraphicsLayoutWidget>`.
14 15 16 17 18 19 20 21 22 23
    """


    def __init__(self, parent=None, border=None):
        GraphicsWidget.__init__(self, parent)
        if border is True:
            border = (100,100,100)
        self.border = border
        self.layout = QtGui.QGraphicsGridLayout()
        self.setLayout(self.layout)
24 25
        self.items = {}  ## item: [(row, col), (row, col), ...]  lists all cells occupied by the item
        self.rows = {}   ## row: {col1: item1, col2: item2, ...}    maps cell location to item
26 27
        self.currentRow = 0
        self.currentCol = 0
28 29 30 31 32 33
        self.setSizePolicy(QtGui.QSizePolicy(QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Expanding))
    
    #def resizeEvent(self, ev):
        #ret = GraphicsWidget.resizeEvent(self, ev)
        #print self.pos(), self.mapToDevice(self.rect().topLeft())
        #return ret
34 35 36 37
    
    def nextRow(self):
        """Advance to next row for automatic item placement"""
        self.currentRow += 1
38 39
        self.currentCol = -1
        self.nextColumn()
40
        
41 42
    def nextColumn(self):
        """Advance to next available column
43
        (generally only for internal use--called by addItem)"""
44 45 46
        self.currentCol += 1
        while self.getItem(self.currentRow, self.currentCol) is not None:
            self.currentCol += 1
47
        
48
    def nextCol(self, *args, **kargs):
Luke Campagnola's avatar
Luke Campagnola committed
49
        """Alias of nextColumn"""
50 51
        return self.nextColumn(*args, **kargs)
        
52
    def addPlot(self, row=None, col=None, rowspan=1, colspan=1, **kargs):
Luke Campagnola's avatar
Luke Campagnola committed
53 54 55 56 57
        """
        Create a PlotItem and place it in the next available cell (or in the cell specified)
        All extra keyword arguments are passed to :func:`PlotItem.__init__ <pyqtgraph.PlotItem.__init__>`
        Returns the created item.
        """
58 59 60 61 62
        plot = PlotItem(**kargs)
        self.addItem(plot, row, col, rowspan, colspan)
        return plot
        
    def addViewBox(self, row=None, col=None, rowspan=1, colspan=1, **kargs):
Luke Campagnola's avatar
Luke Campagnola committed
63 64 65 66 67
        """
        Create a ViewBox and place it in the next available cell (or in the cell specified)
        All extra keyword arguments are passed to :func:`ViewBox.__init__ <pyqtgraph.ViewBox.__init__>`
        Returns the created item.
        """
68 69 70 71
        vb = ViewBox(**kargs)
        self.addItem(vb, row, col, rowspan, colspan)
        return vb
        
72
    def addLabel(self, text=' ', row=None, col=None, rowspan=1, colspan=1, **kargs):
Luke Campagnola's avatar
Luke Campagnola committed
73 74 75 76
        """
        Create a LabelItem with *text* and place it in the next available cell (or in the cell specified)
        All extra keyword arguments are passed to :func:`LabelItem.__init__ <pyqtgraph.LabelItem.__init__>`
        Returns the created item.
77 78
        
        To create a vertical label, use *angle*=-90
Luke Campagnola's avatar
Luke Campagnola committed
79
        """
80 81 82 83
        text = LabelItem(text, **kargs)
        self.addItem(text, row, col, rowspan, colspan)
        return text
        
84
    def addLayout(self, row=None, col=None, rowspan=1, colspan=1, **kargs):
Luke Campagnola's avatar
Luke Campagnola committed
85 86 87 88 89
        """
        Create an empty GraphicsLayout and place it in the next available cell (or in the cell specified)
        All extra keyword arguments are passed to :func:`GraphicsLayout.__init__ <pyqtgraph.GraphicsLayout.__init__>`
        Returns the created item.
        """
90 91 92 93
        layout = GraphicsLayout(**kargs)
        self.addItem(layout, row, col, rowspan, colspan)
        return layout
        
94
    def addItem(self, item, row=None, col=None, rowspan=1, colspan=1):
Luke Campagnola's avatar
Luke Campagnola committed
95 96 97 98
        """
        Add an item to the layout and place it in the next available cell (or in the cell specified).
        The item must be an instance of a QGraphicsWidget subclass.
        """
99 100 101
        if row is None:
            row = self.currentRow
        if col is None:
102
            col = self.currentCol
103
            
104 105 106 107 108 109 110 111 112
        self.items[item] = []
        for i in range(rowspan):
            for j in range(colspan):
                row2 = row + i
                col2 = col + j
                if row2 not in self.rows:
                    self.rows[row2] = {}
                self.rows[row2][col2] = item
                self.items[item].append((row2, col2))
113 114
        
        self.layout.addItem(item, row, col, rowspan, colspan)
115
        self.nextColumn()
116 117

    def getItem(self, row, col):
118 119
        """Return the item in (*row*, *col*). If the cell is empty, return None."""
        return self.rows.get(row, {}).get(col, None)
120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138

    def boundingRect(self):
        return self.rect()
        
    def paint(self, p, *args):
        if self.border is None:
            return
        p.setPen(fn.mkPen(self.border))
        for i in self.items:
            r = i.mapRectToParent(i.boundingRect())
            p.drawRect(r)
    
    def itemIndex(self, item):
        for i in range(self.layout.count()):
            if self.layout.itemAt(i).graphicsItem() is item:
                return i
        raise Exception("Could not determine index of item " + str(item))
    
    def removeItem(self, item):
Luke Campagnola's avatar
Luke Campagnola committed
139
        """Remove *item* from the layout."""
140 141 142
        ind = self.itemIndex(item)
        self.layout.removeAt(ind)
        self.scene().removeItem(item)
143 144 145
        
        for r,c in self.items[item]:
            del self.rows[r][c]
146 147 148 149 150
        del self.items[item]
        self.update()
    
    def clear(self):
        items = []
151
        for i in list(self.items.keys()):
152 153 154
            self.removeItem(i)