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
        
Luke Campagnola's avatar
Luke Campagnola committed
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)