def test_two_color(self): generator = GradientPaletteGenerator('#000', '#fff') np.testing.assert_equal(generator.getRGB(.5), (128, 128, 128)) np.testing.assert_equal(generator.getRGB(np.nan), NAN_GREY) np.testing.assert_equal(generator.getRGB([0, .5, np.nan]), [(0, 0, 0), (128, 128, 128), NAN_GREY])
def test_three_color(self): generator = GradientPaletteGenerator('#f00', '#000', '#fff') np.testing.assert_equal(generator.getRGB(.5), (0, 0, 0)) np.testing.assert_equal(generator.getRGB(np.nan), NAN_GREY) np.testing.assert_equal(generator.getRGB([0, .5, 1]), [(255, 0, 0), (0, 0, 0), (255, 255, 255)])
def test_three_color(self): generator = GradientPaletteGenerator('#f00', '#000', '#fff') for float_values, rgb in ((.5, (0, 0, 0)), (np.nan, NAN_GREY), ((0, .5, 1), ((255, 0, 0), (0, 0, 0), (255, 255, 255)))): np.testing.assert_equal(generator.getRGB(float_values), rgb)
def test_two_color(self): generator = GradientPaletteGenerator('#000', '#fff') for float_values, rgb in ((.5, (128, 128, 128)), (np.nan, NAN_GREY), ((0, .5, np.nan), ((0, 0, 0), (128, 128, 128), NAN_GREY))): np.testing.assert_equal(generator.getRGB(float_values), rgb)
def test_three_color(self): generator = GradientPaletteGenerator("#f00", "#000", "#fff") for float_values, rgb in ( (0.5, (0, 0, 0)), (np.nan, NAN_GREY), ((0, 0.5, 1), ((255, 0, 0), (0, 0, 0), (255, 255, 255))), ): np.testing.assert_equal(generator.getRGB(float_values), rgb)
def test_two_color(self): generator = GradientPaletteGenerator("#000", "#fff") for float_values, rgb in ( (0.5, (128, 128, 128)), (np.nan, NAN_GREY), ((0, 0.5, np.nan), ((0, 0, 0), (128, 128, 128), NAN_GREY)), ): np.testing.assert_equal(generator.getRGB(float_values), rgb)
def setSeries(self, timeseries, attr, xdim, ydim, fagg): if timeseries is None or not attr: self.clear() return if isinstance(xdim, str) and xdim.isdigit(): xdim = [str(i) for i in range(1, int(xdim) + 1)] if isinstance(ydim, str) and ydim.isdigit(): ydim = [str(i) for i in range(1, int(ydim) + 1)] if isinstance(xdim, DiscreteVariable): xcol = timeseries.get_column_view(xdim)[0] xvals, xfunc = xdim.values, lambda i, _: xdim.repr_val(xcol[i]) else: xvals, xfunc = xdim.value if isinstance(ydim, DiscreteVariable): ycol = timeseries.get_column_view(ydim)[0] yvals, yfunc = ydim.values, lambda i, _: ydim.repr_val(ycol[i]) else: yvals, yfunc = ydim.value attr = attr[0] values = timeseries.get_column_view(attr)[0] time_values = [ fromtimestamp(i, tz=timeseries.time_variable.timezone) for i in timeseries.time_values ] if not yvals: yvals = sorted( set( yfunc(i, v) for i, v in enumerate(time_values) if v is not None)) if not xvals: xvals = sorted( set( xfunc(i, v) for i, v in enumerate(time_values) if v is not None)) indices = defaultdict(list) for i, tval in enumerate(time_values): if tval is not None: indices[(xfunc(i, tval), yfunc(i, tval))].append(i) if self._owwidget.invert_date_order: yvals = yvals[::-1] series = [] aggvals = [] self.indices = [] xname = self.AxesCategories.name_it(xdim) yname = self.AxesCategories.name_it(ydim) for yval in yvals: data = [] series.append(dict(name=yname(yval), data=data)) self.indices.append([]) for xval in xvals: inds = indices.get((xval, yval), ()) self.indices[-1].append(inds) point = dict(y=1) data.append(point) if inds: try: aggval = np.round(fagg(values[inds]), 4) except ValueError: aggval = np.nan else: aggval = np.nan if isinstance(aggval, Number) and np.isnan(aggval): aggval = 'N/A' point['select'] = '' point['color'] = 'white' else: aggvals.append(aggval) point['n'] = aggval # TODO: allow scaling over just rows or cols instead of all values as currently try: maxval, minval = np.max(aggvals), np.min(aggvals) except ValueError: self.clear() return ptpval = maxval - minval color = GradientPaletteGenerator('#ffcccc', '#cc0000') selected_color = GradientPaletteGenerator('#cdd1ff', '#0715cd') for serie in series: for point in serie['data']: n = point['n'] if isinstance(n, Number): val = (n - minval) / ptpval if attr.is_discrete: point['n'] = attr.repr_val(n) elif isinstance(attr, TimeVariable): point['n'] = attr.repr_val(n) if attr.is_discrete: point['color'] = color = color_to_hex( attr.colors[int(n)]) sel_color = QColor(color).darker(150).name() else: point['color'] = color[val] sel_color = selected_color[val] point['states'] = dict( select=dict(borderColor="black", color=sel_color)) # TODO: make a white hole in the middle. Center w/o data. self.chart(series=series, xAxis_categories=[xname(i) for i in xvals], yAxis_categories=[yname(i) for i in reversed(yvals)], javascript_after=''' // Force zoomType which is by default disabled for polar charts chart.options.chart.zoomType = 'xy'; chart.pointer.init(chart, chart.options); ''')
def non_reentrant(func): """Prevent the function from reentry.""" lock = Lock() @wraps(func) def locker(*args, **kwargs): if lock.acquire(False): try: return func(*args, **kwargs) finally: lock.release() return locker CONTINUOUS_PALETTE = GradientPaletteGenerator('#00ffff', '#550066') class SelectionMode: NONE, \ SEARCH, \ NEIGHBORS, \ AT_LEAST_N, \ AT_MOST_N, \ ANY_NEIGH, \ AVG_NEIGH, \ MOST_CONN, \ FROM_INPUT \ = range(9) # FML
def paintEvent(self, event): """Adapted from http://doc.qt.io/qt-5/qtwidgets-widgets-analogclock-example.html""" HOURHAND = QPolygon([QPoint(7, 8), QPoint(-7, 8), QPoint(0, -55)]) MINUTEHAND = QPolygon([QPoint(7, 8), QPoint(-7, 8), QPoint(0, -87)]) HOURCOLOR = QColor(Qt.black) MINUTECOLOR = QColor(0x11, 0x11, 0x11, 0xAA) painter = QPainter(self) painter.setRenderHint(QPainter.Antialiasing) painter.translate(self.width() / 2, self.height() / 2) SIDE = 200 side = min(self.width(), self.height()) painter.scale(side / SIDE, side / SIDE) # Background (night/day) if self._time_to is not None: time = self._time_to.time() hour_offset = time.hour() + time.minute() / 60 DAY, NIGHT = QColor(Qt.white), QColor('#5555ff') if 7 <= hour_offset <= 19: background = DAY elif 6 <= hour_offset <= 7: palette = GradientPaletteGenerator(NIGHT, DAY) background = palette[(hour_offset - 6) / (7 - 6)] elif 19 <= hour_offset <= 20: palette = GradientPaletteGenerator(DAY, NIGHT) background = palette[(hour_offset - 19) / (20 - 19)] else: assert hour_offset < 7 or hour_offset > 20 background = NIGHT painter.setBrush(QBrush(background)) painter.setPen(HOURCOLOR) painter.drawEllipse(-SIDE / 2, -SIDE / 2, SIDE, SIDE) # Minute tickmarks painter.save() painter.setPen(MINUTECOLOR) for j in range(60): painter.drawLine(94, 0, 97, 0) painter.rotate(6) painter.restore() # Hour tickmarks painter.save() painter.setPen(HOURCOLOR) for _ in range(12): painter.drawLine(88, 0, 98, 0) painter.rotate(30) painter.restore() # Hour span if self._time_from is not None: time_from = self._time_from.time() time_to = self._time_to.time() if time_from.secsTo( time_to) / 3600 > .2: # Don't draw really small intervals hour_from = (time_from.hour() + time_from.minute() / 60) % 12 - 3 hour_to = (time_to.hour() + time_to.minute() / 60) % 12 - 3 startAngle = -hour_to * 30 * 16 spanAngle = -hour_from * 30 * 16 - startAngle color = QColor(0xFF, 0xFF, 0, 0xAA) painter.save() painter.setBrush(QBrush(color, Qt.DiagCrossPattern)) painter.setPen(color.darker(180)) painter.drawPie(-SIDE / 2, -SIDE / 2, SIDE, SIDE, startAngle, spanAngle) painter.restore() # Hour and minute hand if self._time_to is not None: time = self._time_to.time() painter.setPen(Qt.NoPen) painter.save() painter.setBrush(HOURCOLOR) painter.rotate(30 * (time.hour() + time.minute() / 60)) painter.drawConvexPolygon(HOURHAND) painter.restore() painter.save() painter.setBrush(MINUTECOLOR) painter.rotate(6 * (time.minute() + time.second() / 60)) painter.drawConvexPolygon(MINUTEHAND) painter.restore()
def setSeries(self, timeseries, attr, xdim, ydim, fagg): if timeseries is None or not attr: self.clear() return # TODO: support discrete variables if isinstance(xdim, str) and xdim.isdigit(): xdim = [str(i) for i in range(1, int(xdim) + 1)] if isinstance(ydim, str) and ydim.isdigit(): ydim = [str(i) for i in range(1, int(ydim) + 1)] xvals, xfunc = xdim.value yvals, yfunc = ydim.value values = Timeseries(Domain([], [], attr, source=timeseries.domain), timeseries).metas time_values = np.ravel(timeseries[:, timeseries.time_variable]) if True: fromtimestamp = datetime.fromtimestamp time_values = [fromtimestamp(i) for i in time_values] if not yvals: yvals = sorted(set(yfunc(i) for i in time_values)) if not xvals: xvals = sorted(set(xfunc(i) for i in time_values)) indices = defaultdict(list) for i, tval in enumerate(time_values): indices[(xfunc(tval), yfunc(tval))].append(i) series = [] aggvals = [] self.indices = [] xname = self.AxesCategories.name_it(xdim) yname = self.AxesCategories.name_it(ydim) for yval in yvals: data = [] series.append(dict(name=yname(yval), data=data)) self.indices.append([]) for xval in xvals: inds = indices.get((xval, yval), ()) self.indices[-1].append(inds) point = dict(y=1) data.append(point) if inds: try: aggval = fagg(values[inds]) except ValueError: aggval = np.nan else: aggval = np.nan if np.isnan(aggval): aggval = 'NaN' point['select'] = '' point['color'] = 'white' else: aggvals.append(aggval) point['n'] = aggval # TODO: allow scaling over just rows or cols instead of all values as currently try: maxval, minval = np.max(aggvals), np.min(aggvals) except ValueError: self.clear() return ptpval = maxval - minval color = GradientPaletteGenerator('#ffcccc', '#cc0000') selected_color = GradientPaletteGenerator('#ccffcc', '#006600') for serie in series: for point in serie['data']: n = point['n'] if isinstance(n, Number): val = (n - minval) / ptpval point['color'] = color[val] point['states'] = dict(select=dict(color=selected_color[val])) # TODO: make a white hole in the middle. Center w/o data. self.chart(series=series, xAxis_categories=[xname(i) for i in xvals], yAxis_categories=[yname(i) for i in reversed(yvals)])