Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Legend bar #10

Open
wants to merge 16 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions python/lognplot/chart/__init__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from .axis import Axis
from .chart import Chart
from .curve import Curve
from .legend import Legend, LegendMode
from .logbar import LogTrack, LogBar
from .event_tracks import EventTracks
9 changes: 9 additions & 0 deletions python/lognplot/chart/chart.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import math
from .axis import Axis
from .curve import Curve
from .legend import Legend
from ..utils import bench_it
from ..time import TimeSpan
from ..tsdb import Aggregation, Metrics
Expand All @@ -17,7 +18,9 @@ class Chart:
def __init__(self, db):
self.x_axis = Axis()
self.y_axis = Axis()
self.legend = Legend()
self.curves = []
self.activeCurve = None
self.cursor = None
self.db = db

Expand All @@ -31,9 +34,15 @@ def add_curve(self, name, color):
if not self.has_curve(name):
curve = Curve(self.db, name, color)
self.curves.append(curve)
self.change_active_curve(curve)

def clear_curves(self):
self.curves.clear()
self.y_axis = Axis()

def change_active_curve(self, curve):
self.activeCurve = curve
self.y_axis = self.activeCurve.axis

def info(self):
print(f"Chart with {len(self.curves)} series")
Expand Down
8 changes: 7 additions & 1 deletion python/lognplot/chart/curve.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
from ..tsdb.aggregation import Aggregation

from .axis import Axis

class Curve:
""" A curve is a view onto a signal in the database.
Expand All @@ -14,6 +14,12 @@ def __init__(self, db, name, color):
self._db = db
self.name = name
self.color = color
# Corresponding handle (polygon area)
self.handle = []
# Corresponding legend segment (polygon area)
self.legend_segment = []
# Each curve has its own vertical axis
self.axis = Axis()

def __repr__(self):
return "Database proxy-curve"
Expand Down
21 changes: 21 additions & 0 deletions python/lognplot/chart/legend.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
from enum import Enum

class LegendMode(Enum):
SIGNAL_NAMES = 0
CURSOR_VALUES = 1
Y_AXIS_SCALE = 2

class Legend:

def __init__(self):
self.modes = [LegendMode.SIGNAL_NAMES,
LegendMode.CURSOR_VALUES,
LegendMode.Y_AXIS_SCALE]
self.active_mode = 0

def next_mode(self):
self.active_mode = (self.active_mode + 1) % len(self.modes)

@property
def mode(self) -> LegendMode:
return self.modes[self.active_mode]
8 changes: 4 additions & 4 deletions python/lognplot/qt/render/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ def calc_y_ticks(self, axis):
y_ticks = axis.get_ticks(amount_y_ticks)
return y_ticks

def draw_grid(self, x_ticks, y_ticks):
def draw_grid(self, y_axis, x_ticks, y_ticks):
""" Render a grid on the given x and y tick markers. """
pen = QtGui.QPen(Qt.gray)
pen.setWidth(1)
Expand All @@ -46,7 +46,7 @@ def draw_grid(self, x_ticks, y_ticks):
self.painter.drawLine(x, self.layout.chart_top, x, self.layout.chart_bottom)

for value, _ in y_ticks:
y = self.to_y_pixel(value)
y = self.to_y_pixel(y_axis, value)
self.painter.drawLine(self.layout.chart_left, y, self.layout.chart_right, y)

def draw_x_axis(self, x_ticks):
Expand All @@ -69,15 +69,15 @@ def draw_x_axis(self, x_ticks):
text_y = y + 10 - text_rect.y()
self.painter.drawText(text_x, text_y, label)

def draw_y_axis(self, y_ticks):
def draw_y_axis(self, y_axis, y_ticks):
""" Draw the Y-axis. """
pen = QtGui.QPen(Qt.black)
pen.setWidth(2)
self.painter.setPen(pen)
x = self.layout.chart_right + 5
self.painter.drawLine(x, self.layout.chart_top, x, self.layout.chart_bottom)
for value, label in y_ticks:
y = self.to_y_pixel(value)
y = self.to_y_pixel(y_axis, value)

# Tick handle:
self.painter.drawLine(x, y, x + 5, y)
Expand Down
118 changes: 61 additions & 57 deletions python/lognplot/qt/render/chart.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
from ..qtapi import QtGui, QtCore, Qt
from ...chart import Chart
from ...chart import Axis, Chart
from ...utils import bench_it
from ...tsdb import Aggregation
from .layout import ChartLayout
Expand All @@ -25,16 +25,19 @@ def render(self):
y_ticks = self.calc_y_ticks(self.chart.y_axis)

if self.options.show_grid:
self.draw_grid(x_ticks, y_ticks)
self.draw_grid(self.chart.y_axis, x_ticks, y_ticks)

self.draw_bouding_rect()

if self.options.show_axis:
self.draw_x_axis(x_ticks)
self.draw_y_axis(y_ticks)
self.draw_y_axis(self.chart.y_axis, y_ticks)

if self.options.show_handles:
self._draw_handles()

self._draw_curves()
self._draw_legend()

self._draw_cursor()

def shade_region(self, region):
Expand Down Expand Up @@ -77,17 +80,17 @@ def _draw_curve(self, curve):

if data:
if isinstance(data[0], Aggregation):
self._draw_aggregations_as_shape(data, curve_color)
self._draw_aggregations_as_shape(curve.axis, data, curve_color)
else:
self._draw_samples_as_lines(data, curve_color)
self._draw_samples_as_lines(curve.axis, data, curve_color)

def _draw_samples_as_lines(self, samples, curve_color: QtGui.QColor):
def _draw_samples_as_lines(self, y_axis: Axis, samples, curve_color: QtGui.QColor):
""" Draw raw samples as lines! """
pen = QtGui.QPen(curve_color)
pen.setWidth(2)
self.painter.setPen(pen)
points = [
QtCore.QPoint(self.to_x_pixel(x), self.to_y_pixel(y)) for (x, y) in samples
QtCore.QPoint(self.to_x_pixel(x), self.to_y_pixel(y_axis, y)) for (x, y) in samples
]
line = QtGui.QPolygon(points)
self.painter.drawPolyline(line)
Expand All @@ -98,7 +101,7 @@ def _draw_samples_as_lines(self, samples, curve_color: QtGui.QColor):
self.painter.drawEllipse(rect)

def _draw_aggregations_as_shape(
self, aggregations: Aggregation, curve_color: QtGui.QColor
self, y_axis: Axis, aggregations: Aggregation, curve_color: QtGui.QColor
):
""" Draw aggregates as polygon shapes.

Expand All @@ -119,30 +122,30 @@ def _draw_aggregations_as_shape(
# x2 = self.to_x_pixel(metric.x2)

# max line:
y_max = self.to_y_pixel(aggregation.metrics.maximum)
y_max = self.to_y_pixel(y_axis, aggregation.metrics.maximum)
max_points.append(QtCore.QPoint(x1, y_max))
# max_points.append(QtCore.QPoint(x2, y_max))

# min line:
y_min = self.to_y_pixel(aggregation.metrics.minimum)
y_min = self.to_y_pixel(y_axis, aggregation.metrics.minimum)
min_points.append(QtCore.QPoint(x1, y_min))
# min_points.append(QtCore.QPoint(x2, y_min))

mean = aggregation.metrics.mean
stddev = aggregation.metrics.stddev

# Mean line:
y_mean = self.to_y_pixel(mean)
y_mean = self.to_y_pixel(y_axis, mean)
mean_points.append(QtCore.QPoint(x1, y_mean))
# mean_points.append(QtCore.QPoint(x2, y_mean))

# stddev up line:
y_stddev_up = self.to_y_pixel(mean + stddev)
y_stddev_up = self.to_y_pixel(y_axis, mean + stddev)
stddev_up_points.append(QtCore.QPoint(x1, y_stddev_up))
# stddev_up_points.append(QtCore.QPoint(x2, y_stddev_up))

# stddev down line:
y_stddev_down = self.to_y_pixel(mean - stddev)
y_stddev_down = self.to_y_pixel(y_axis, mean - stddev)
stddev_down_points.append(QtCore.QPoint(x1, y_stddev_down))
# stddev_down_points.append(QtCore.QPoint(x2, y_stddev_down))

Expand Down Expand Up @@ -188,31 +191,6 @@ def _draw_aggregations_as_shape(
min_line = QtGui.QPolygon(min_points)
self.painter.drawPolyline(min_line)

def _draw_legend(self):
""" Draw names / color of the curve next to eachother.
"""
font_metrics = self.painter.fontMetrics()
x = self.layout.chart_left + 10
y = self.layout.chart_top + 10
text_height = font_metrics.height()
color_block_size = text_height * 0.8
for index, curve in enumerate(self.chart.curves):
color = QtGui.QColor(curve.color)
text = curve.name
text_rect = font_metrics.boundingRect(text)
legend_x = x
legend_y = y + index * text_height
text_x = legend_x + color_block_size + 3 - text_rect.x()
text_y = legend_y - text_rect.y() - text_rect.height() / 2
self.painter.drawText(text_x, text_y, text)
self.painter.fillRect(
x,
legend_y - color_block_size / 2,
color_block_size,
color_block_size,
color,
)

def _draw_cursor(self):
if self.chart.cursor:
# Draw cursor line:
Expand Down Expand Up @@ -240,7 +218,7 @@ def _draw_cursor(self):
pen.setWidth(2)
self.painter.setPen(pen)
marker_x = self.to_x_pixel(curve_point_timestamp)
marker_y = self.to_y_pixel(curve_point_value)
marker_y = self.to_y_pixel(curve.axis, curve_point_value)
marker_size = 10
indicator_rect = QtCore.QRect(
marker_x - marker_size // 2,
Expand All @@ -251,27 +229,53 @@ def _draw_cursor(self):
self.painter.drawEllipse(indicator_rect)

# Legend:
text = "{} = {}".format(curve.name, curve_point_value)
text_rect = font_metrics.boundingRect(text)
# legend_y = y + index * text_height
legend_x = marker_x + 10
legend_y = marker_y
text_x = legend_x + color_block_size + 3 - text_rect.x()
text_y = legend_y - text_rect.y() - text_rect.height() / 2
self.painter.drawText(text_x, text_y, text)
self.painter.fillRect(
legend_x,
legend_y - color_block_size / 2,
color_block_size,
color_block_size,
color,
)
if self.options.show_cursor_legend:
text = "{} = {}".format(curve.name, curve_point_value)
text_rect = font_metrics.boundingRect(text)
# legend_y = y + index * text_height
legend_x = marker_x + 10
legend_y = marker_y
text_x = legend_x + color_block_size + 3 - text_rect.x()
text_y = legend_y - text_rect.y() - text_rect.height() / 2
self.painter.drawText(text_x, text_y, text)
self.painter.fillRect(
legend_x,
legend_y - color_block_size / 2,
color_block_size,
color_block_size,
color,
)

def _draw_handles(self):
x = self.layout.handles.left()

for _, curve in enumerate(self.chart.curves):
handle_y = self.to_y_pixel(curve.axis, 0)
x_full = self.options.handle_width
x_half = x_full / 2
y_half = self.options.handle_height / 2

curve.handle = [
QtCore.QPointF(x, handle_y - y_half),
QtCore.QPointF(x, handle_y - y_half),
QtCore.QPointF(x + x_half, handle_y - y_half),
QtCore.QPointF(x + x_full, handle_y),
QtCore.QPointF(x + x_half, handle_y + y_half),
QtCore.QPointF(x, handle_y + y_half)
]

polygon = QtGui.QPainterPath(curve.handle[0])
for p in curve.handle[1:]:
polygon.lineTo(p)

color = QtGui.QColor(curve.color)
self.painter.fillPath(polygon, QtGui.QBrush(color))

def to_x_pixel(self, value):
return transform.to_x_pixel(value, self.chart.x_axis, self.layout)

def to_y_pixel(self, value):
return transform.to_y_pixel(value, self.chart.y_axis, self.layout)
def to_y_pixel(self, y_axis, value):
return transform.to_y_pixel(value, y_axis, self.layout)

def x_pixel_to_domain(self, pixel):
axis = self.chart.x_axis
Expand Down
23 changes: 21 additions & 2 deletions python/lognplot/qt/render/layout.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,34 @@ def __init__(self, rect: QtCore.QRect, options):
def do_layout(self):
# self.right = self.rect.right()
# self.bottom = self.rect.bottom()
self.chart_top = self.rect.top() + self.options.padding
self.chart_left = self.rect.left() + self.options.padding
if self.options.show_axis:
axis_height = self.axis_height
axis_width = self.axis_width
else:
axis_height = 0
axis_width = 0

if self.options.show_legend:
self.legend = QtCore.QRect(self.rect.left() + self.options.padding,
self.rect.top() + self.options.padding,
self.rect.right() - 2 * self.options.padding,
self.options.legend_height)

self.chart_top = self.legend.bottom() + 10
else:
self.chart_top = self.rect.top() + self.options.padding

self.handles = QtCore.QRect(self.rect.left() + self.options.padding,
self.rect.top(),
self.options.handle_width,
self.rect.height())

if self.options.show_handles:
self.chart_left = self.handles.right() + 3
else:
self.chart_left = self.rect.left() + self.options.padding


self.chart_bottom = self.rect.bottom() - self.options.padding - axis_height
self.chart_right = self.rect.right() - self.options.padding - axis_width
self.chart_width = self.chart_right - self.chart_left
Expand Down
Loading