-
Notifications
You must be signed in to change notification settings - Fork 11
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
Signal handles and multi-axis support #9
base: master
Are you sure you want to change the base?
Changes from all commits
75375fe
9639985
ce4ce7c
9a4ffd7
50e41e8
38e3cf7
28336ad
a364b18
d2b5dec
4de4efc
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
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 | ||
|
@@ -25,16 +25,21 @@ 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() | ||
|
||
if self.options.show_legend: | ||
self._draw_legend() | ||
self._draw_cursor() | ||
|
||
def shade_region(self, region): | ||
|
@@ -77,17 +82,17 @@ def _draw_curve(self, curve): | |
|
||
if data: | ||
if isinstance(data[0], Aggregation): | ||
self._draw_aggregations_as_shape(data, curve_color) | ||
curve.average = self._draw_aggregations_as_shape(curve.axis, data, curve_color) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You could query the average from by querying the summary of the data in view to the time series database (tsdb). This will give you a quicker result than re-calculating the average in the draw function. Also: having the draw function doing two things (draw the plot and calculate the average) is probably not so clean, it would be better to seperate the two functions. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sure, still have to get familiar with the codebase. |
||
else: | ||
self._draw_samples_as_lines(data, curve_color) | ||
curve.average = 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) | ||
|
@@ -97,8 +102,10 @@ def _draw_samples_as_lines(self, samples, curve_color: QtGui.QColor): | |
rect = QtCore.QRect(point.x() - 3, point.y() - 3, 6, 6) | ||
self.painter.drawEllipse(rect) | ||
|
||
return sum(p.y() for p in points) / len(points) | ||
|
||
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. | ||
|
||
|
@@ -119,30 +126,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)) | ||
|
||
|
@@ -188,6 +195,8 @@ def _draw_aggregations_as_shape( | |
min_line = QtGui.QPolygon(min_points) | ||
self.painter.drawPolyline(min_line) | ||
|
||
return (sum(p.y() for p in mean_points) / len(mean_points)) | ||
|
||
def _draw_legend(self): | ||
""" Draw names / color of the curve next to eachother. | ||
""" | ||
|
@@ -240,7 +249,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, | ||
|
@@ -267,11 +276,36 @@ def _draw_cursor(self): | |
color, | ||
) | ||
|
||
def _draw_handles(self): | ||
x = self.layout.handles.left() | ||
|
||
for _, curve in enumerate(self.chart.curves): | ||
handle_y = curve.average | ||
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 | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -7,7 +7,7 @@ | |
|
||
from ..qtapi import QtCore, QtWidgets, QtGui, Qt, pyqtSignal | ||
from ...utils import bench_it | ||
from ...chart import Chart | ||
from ...chart import Chart, Curve | ||
from ..render import render_chart_on_qpainter, ChartLayout, ChartOptions | ||
from ..render import transform | ||
from . import mime | ||
|
@@ -78,11 +78,34 @@ def mouse_move(self, x, y): | |
self.chart.set_cursor(value) | ||
self.update() | ||
|
||
def curveHandleAtPoint(self, x, y) -> Curve: | ||
for curve in self.chart.curves: | ||
topleft = curve.handle[0] | ||
middleright = curve.handle[3] | ||
bottomleft = curve.handle[-1] | ||
if (x >= topleft.x() and | ||
x <= middleright.x() and | ||
y >= topleft.y() and | ||
y <= bottomleft.y() | ||
): | ||
return curve | ||
return None | ||
|
||
# Mouse interactions: | ||
def mousePress(self, x, y): | ||
curve = self.curveHandleAtPoint(x,y) | ||
if curve is not None: | ||
self._drag_handle = curve | ||
self.chart.change_active_curve(curve) | ||
|
||
def pan(self, dx, dy): | ||
# print("pan", dx, dy) | ||
shift = transform.x_pixels_to_domain(dx, self.chart.x_axis, self.chart_layout) | ||
self.chart.horizontal_pan_absolute(-shift) | ||
self.chart.autoscale_y() | ||
if self.chart_options.autoscale_y_axis: | ||
self.chart.autoscale_y() | ||
else: | ||
self._drag_handle.axis.pan_relative(dy / self.rect().height()) | ||
self.update() | ||
|
||
def add_curve(self, name, color=None): | ||
|
@@ -109,16 +132,19 @@ def horizontal_zoom(self, amount, around): | |
self.chart.horizontal_zoom(amount, around) | ||
# Autoscale Y for a nice effect? | ||
self.chart.autoscale_y() | ||
self.repaint() | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What does this function do? update also triggers a paint action eventually. Does this result in snappier look / feel? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The handles on the left hand side otherwise lagged behind in the repainting. Not sure about the reason though. Maybe that 'update' only invalidated the chart area itself? |
||
self.update() | ||
|
||
def vertical_zoom(self, amount): | ||
self.chart.vertical_zoom(amount) | ||
self.repaint() | ||
self.update() | ||
|
||
def horizontal_pan(self, amount): | ||
self.chart.horizontal_pan_relative(amount) | ||
# Autoscale Y for a nice effect? | ||
self.chart.autoscale_y() | ||
self.repaint() | ||
self.update() | ||
|
||
def vertical_pan(self, amount): | ||
|
@@ -128,12 +154,14 @@ def vertical_pan(self, amount): | |
def zoom_fit(self): | ||
""" Autoscale all in fit! """ | ||
self.chart.zoom_fit() | ||
self.repaint() | ||
self.update() | ||
|
||
def zoom_to_last(self, span): | ||
""" Zoom to fit the last x time in view. | ||
""" | ||
self.chart.zoom_to_last(span) | ||
self.repaint() | ||
self.update() | ||
|
||
def enable_tailing(self, timespan): | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This option certainly makes sense.