LASP 1.0
Library for Acoustic Signal Processing
Loading...
Searching...
No Matches
bar.py
Go to the documentation of this file.
1#!/usr/bin/env python3
2# -*- coding: utf-8 -*-
3
4"""
5Author: J.A. de Jong - ASCEE
6
7Description:
8Class for plotting bars on a QGraphicsScene.
9
10"""
11from ..lasp_gui_tools import ASCEEColors, Branding
12from PySide.QtGui import (
13 QGraphicsScene, QPen, QBrush, QGraphicsRectItem,
14 QGraphicsTextItem, QPainter, QImage, QPrinter
15 )
16
17from PySide.QtCore import Qt, QRectF, QLineF, QSize, QRect, QPointF, QSizeF
18import numpy as np
19import os
20
21
22leftoffset = 120 # Left offset of the figure
23rightoffset = 60
24topoffset = 30
25bottomoffset = 80
26xticklabeloffset = 55
27xlabelbottomoffset = 30
28ylabelleftoffset = 30
29nyticks = 11
30ticklength = 10
31
32# Distance between two bar groups in units of bar thicknesses
33dxbars = 2
34
35DEFAULT_COLORS = [ASCEEColors.blue, ASCEEColors.green, Qt.red, Qt.cyan,
36 Qt.darkYellow,
37 Qt.darkMagenta]
38
39
41 item = QGraphicsTextItem(label)
42 item.setFont(Branding.figureFont())
43 return item
44
45
46class BarScene(QGraphicsScene):
47 """
48 Graphhics Scene for plotting bars
49 """
50
51 def __init__(self, parent, xvals, G, ylim=(0, 1),
52 grid=True,
53 xlabel=None,
54 ylabel=None,
55 title=None,
56 colors=DEFAULT_COLORS, size=(1200, 600),
57 legend=None,
58 legendpos=None):
59 """
60 Initialize a bar scene
61
62 Args:
63 xvals: labels and x positions of the bars
64 G: Number of bars per x value
65 ylim: y limits of the figure
66 xlabel: label below x-axis
67 ylabel: label on left side of the y-axis
68 title: figure title
69 colors: color cycler
70 size: size of the plot in pixels
71 legend: list of legend strings to show.
72 legendpos: position of legend w.r.t. default position, in pixels
73 """
74 super().__init__(parent=parent)
75 self.setSceneRect(QRect(0, 0, *size))
76
77 # self.setBackgroundBrush(ASCEEColors.bgBrush(0, size[0]))
78 self.ylim = ylim
79 N = len(xvals)
80 self.N = N
81 self.G = G
82 self.bgs = []
83
84 self.size = size
85 xsize, ysize = size
86
87 self.xsize = xsize
88 self.ysize = ysize
89
90 self.colors = colors
91
92 # Size of the frame
93 Lx = xsize - rightoffset - leftoffset
94 Ly = ysize - topoffset - bottomoffset
95
96 # The main frame where the bars are in.
97 mainframe = self.createRect(leftoffset,
98 bottomoffset,
99 Lx,
100 Ly)
101 # Set the y ticks and ticklabels
102 self.yticks = []
103 txtmaxwidth = 0
104 for i in range(nyticks):
105 y = bottomoffset+Ly*i/(nyticks-1)
106
107 ytick = self.addLine(leftoffset,
108 y,
109 leftoffset-ticklength,
110 y)
111 if grid:
112 ygrid = self.addLine(leftoffset,
113 y,
114 xsize-rightoffset,
115 y, pen=QPen(Qt.gray))
116
117 range_ = ylim[1]-ylim[0]
118 ytickval = i/(nyticks-1)*range_ + ylim[0]
119 yticklabel = f'{ytickval:.0f}'
120 txt = graphicsTextItem(yticklabel)
121 txtwidth = txt.boundingRect().width()
122 txtmaxwidth = max(txtmaxwidth, txtwidth)
123 txt.setPos(leftoffset-10-txtwidth,
124 ysize - y-.022*self.ysize)
125 self.addItem(txt)
126 self.yticks.append(ytick)
127
128 # Main frame added after grid lines, to get the color right
129 self.addItem(mainframe)
130
131 # Create the bars
132 for g in range(G):
133 bg = []
134 for n in range(N):
135 barrect = self.getBarRect(n, g, 0)
136 baritem = QGraphicsRectItem(barrect, brush=QBrush(Qt.blue))
137
138 self.addItem(baritem)
139 bg.append(baritem)
140 self.bgs.append(bg)
141
142 # Add x ticks and ticklabels
143 xticklabels = []
144 for n in range(N):
145 xticklabel = f'{xvals[n]}'
146 txt = graphicsTextItem(xticklabel)
147 txtxpos = self.getBarGroupMidPos(n)-12
148 txt.setPos(txtxpos,
149 self.ysize-bottomoffset+xticklabeloffset)
150 txt.rotate(-90)
151 self.addItem(txt)
152 xticklabels.append(txt)
153
154 # Set xlabel
155 if xlabel is not None:
156 xlabel = graphicsTextItem(xlabel)
157 width = xlabel.boundingRect().width()
158 txtxpos = xsize/2-width/2
159 txtypos = ysize - xlabelbottomoffset
160 xlabel.setPos(txtxpos, txtypos)
161 self.addItem(xlabel)
162
163 # # Set ylabel
164 if ylabel is not None:
165 ylabel = graphicsTextItem(ylabel)
166 ylabel.setPos(ylabelleftoffset,
167 (ysize-topoffset-bottomoffset)/2+topoffset)
168 ylabel.rotate(-90)
169 self.addItem(ylabel)
170
171 # Set title
172 if title is not None:
173 title = graphicsTextItem(title)
174 width = xlabel.boundingRect().width()
175 txtxpos = self.xsize/2-width/2
176 txtypos = (1-.998)*self.ysize
177 title.setPos(txtxpos, txtypos)
178 self.addItem(title)
179
180 if legend is not None:
181 maxlegtxtwidth = 0
182 legposx, legposy = (0, 0) if legendpos is None else legendpos
183
184 legpos = (xsize-rightoffset-300+legposx,
185 ysize-topoffset-30+legposy)
186
187 dyleg = 15
188 dylegtxt = dyleg
189 Lylegrect = 10
190 Lxlegrect = 20
191 legrectmargin = 5
192 boxtopleft = QPointF(legpos[0]-legrectmargin,
193 ysize-legpos[1]-Lylegrect-legrectmargin)
194
195 legbox = self.addRect(QRectF(0, 0, 0, 0),
196 pen=QPen(), brush=QBrush(Qt.white))
197
198 for i, leg in enumerate(legend):
199 leglabel = legend[i]
200
201 # The position of the legend, in screen coordinates
202 pos = (legpos[0], legpos[1] - i*dyleg)
203 color = self.colors[i % len(self.colors)]
204
205 legrect = self.createRect(*pos, Lxlegrect, Lylegrect)
206
207 legrect.setBrush(QBrush(color))
208 legtxt = graphicsTextItem(leglabel)
209 maxlegtxtwidth = max(maxlegtxtwidth,
210 legtxt.boundingRect().width())
211
212 self.addItem(legrect)
213 self.addItem(legtxt)
214
215 legtxt.setPos(legpos[0]+Lxlegrect,
216 ysize-pos[1]-dylegtxt-3)
217
218 legboxsize = QSize(maxlegtxtwidth+Lxlegrect+2*legrectmargin,
219 (i+1)*dyleg+legrectmargin)
220 legboxrect = QRectF(boxtopleft, legboxsize)
221 legbox.setRect(legboxrect)
222
223 def saveAsBitmap(self, fn):
224 """
225 Save bar image as a jpg file. Overwrites a file already existing in
226 filesystem.
227
228 https://stackoverflow.com/questions/7451183/how-to-create-image-file\
229 -from-qgraphicsscene-qgraphicsview#11642517
230
231 Args:
232 fn: Filename
233 Returns:
234 True on success
235 """
236 size = self.size
237 pixelsx = max(1200, size[0])
238 pixelsy = int(pixelsx*size[1]/size[0])
239 imagesize = (pixelsx, pixelsy)
240 image = QImage(pixelsx,
241 pixelsy,
242 QImage.Format_ARGB32_Premultiplied)
243
244 painter = QPainter(image)
245 painter.setRenderHint(QPainter.Antialiasing)
246 painter.setBrush(Qt.white)
247 painter.setPen(Qt.white)
248 painter.drawRect(QRect(0, 0, *imagesize))
249
250 targetrect = QRectF(0, 0, *imagesize)
251 sourcerect = QRectF(0, 0, *size)
252 self.render(painter, targetrect, sourcerect)
253 painter.end()
254
255 return image.save(fn)
256
257 def saveAsPdf(self, fn, force=False):
258 """
259 Save bar image as a eps file.
260
261 Args:
262 fn: Filename
263 force: if True, overwrites an existing file. If false, raises a
264 RuntimeError if file already exists.
265 """
266 printer = QPrinter(QPrinter.HighResolution)
267 printer.setOutputFormat(QPrinter.PdfFormat)
268 printer.setOutputFileName(fn)
269 printer.setFullPage(True)
270 printer.setPageSize(QPrinter.Custom)
271 printer.setPaperSize(QSizeF(*self.size), QPrinter.Millimeter)
272 printer.setPageMargins(0, 0, 0, 0, QPrinter.Millimeter)
273
274 painter = QPainter(printer)
275 painter.setRenderHint(QPainter.Antialiasing)
276 painter.setBrush(Qt.white)
277 painter.setPen(Qt.white)
278 painter.drawRect(QRect(0, 0, *self.size))
279
280 targetrect = QRectF(0, 0, printer.width(), printer.height())
281 sourcerect = QRectF(0, 0, *self.size)
282 self.render(painter, targetrect, sourcerect)
283 painter.end()
284 return True
285
286 def getBarGroupMidPos(self, n):
287 """
288 Returns the mid x position below each bar group
289 """
290 Lx = self.xsize-rightoffset-leftoffset
291 # Ly = self.ysize - topoffset - bottomoffset
292
293 start = 10
294 S = Lx - 2*start
295 L = S/(self.N*self.G+dxbars*(self.N-1))
296 xL = leftoffset+start
297 return (n*(self.G*L+dxbars*L) + xL + self.G*L/2)
298
299 def getBarRect(self, n, g, yval):
300 """
301 Returns a bar QRectF.
302
303 Args:
304 n: Bar index (i.e. corresponding to a certain frequency band)
305 g: Bar group (i.e. corresponding to a certain quantity)
306 yval: Height of bar, 1 for full lenght, 0 for no length
307
308 Returns:
309 QRectF corresponding to the bar at the right place in the scene
310 """
311 assert yval >= 0 and yval <= 1, "Invalid yval"
312 Lx = self.xsize-rightoffset-leftoffset
313 Ly = self.ysize-topoffset - bottomoffset
314
315 start = 10
316 S = Lx - 2*start
317 assert S > 0, "Size of bar field is too small."
318 # Width of a single bar
319 L = S/(self.N*self.G+dxbars*(self.N-1))
320 xL = leftoffset+start
321 x = g*L + n*(self.G*L+dxbars*L) + xL
322
323 return QRectF(x,
324 self.ysize-bottomoffset-yval*Ly,
325 L,
326 yval*Ly)
327
328 def addLine(self, x1, y1, x2, y2, pen=QPen(), brush=QBrush()):
329 line = QLineF(x1,
330 self.ysize - y1,
331 x2,
332 self.ysize - y2)
333 return super().addLine(line, pen=pen, brush=brush)
334
335 def createRect(self, x, y, Lx, Ly, pen=QPen(), brush=QBrush()):
336 """
337 Create a rectangle somewhere, in relative coordinates originating
338 from the lower left position.
339 """
340 x1 = x
341
342 # Y-position from the top, these are the coordinates used to create a
343 # rect item.
344 y1 = self.ysize-y-Ly
345 return QGraphicsRectItem(x1,
346 y1,
347 Lx,
348 Ly,
349 pen=pen,
350 brush=brush)
351
352 def set_ydata(self, newydata):
353 G = len(self.bgs)
354 N = len(self.bgs[0])
355
356 assert newydata.shape[0] == N
357 assert newydata.shape[1] == G
358
359 # Y-values of the bars should be between 0 and 1.
360 scalefac = self.ylim[1]-self.ylim[0]
361 yvals = (newydata - self.ylim[0])/scalefac
362
363 # Clip values to be between 0 and 1
364 yvals = np.clip(yvals, 0, 1)
365
366 for g in range(G):
367 color = self.colors[g % len(self.colors)]
368 for n in range(N):
369 bar = self.bgs[g][n]
370 bar.setRect(self.getBarRect(n, g, yvals[n, g]))
371 bar.setBrush(color)
Graphhics Scene for plotting bars.
Definition bar.py:46
__init__(self, parent, xvals, G, ylim=(0, 1), grid=True, xlabel=None, ylabel=None, title=None, colors=DEFAULT_COLORS, size=(1200, 600), legend=None, legendpos=None)
Initialize a bar scene.
Definition bar.py:58
saveAsBitmap(self, fn)
Save bar image as a jpg file.
Definition bar.py:223
getBarGroupMidPos(self, n)
Returns the mid x position below each bar group.
Definition bar.py:286
saveAsPdf(self, fn, force=False)
Save bar image as a eps file.
Definition bar.py:257
getBarRect(self, n, g, yval)
Returns a bar QRectF.
Definition bar.py:299
addLine(self, x1, y1, x2, y2, pen=QPen(), brush=QBrush())
Definition bar.py:328
createRect(self, x, y, Lx, Ly, pen=QPen(), brush=QBrush())
Create a rectangle somewhere, in relative coordinates originating from the lower left position.
Definition bar.py:335
set_ydata(self, newydata)
Definition bar.py:352
graphicsTextItem(label)
Definition bar.py:40