From 0f6589d9ff613e175aa9f813518206dc41c94d30 Mon Sep 17 00:00:00 2001 From: Tyler Goodlet Date: Mon, 31 Aug 2020 17:18:02 -0400 Subject: [PATCH] Add proper x-axis time-stamping --- piker/ui/_axes.py | 94 ++++++++++++++++++++++++++--------------------- 1 file changed, 52 insertions(+), 42 deletions(-) diff --git a/piker/ui/_axes.py b/piker/ui/_axes.py index 64c5a007..2a705a53 100644 --- a/piker/ui/_axes.py +++ b/piker/ui/_axes.py @@ -1,12 +1,17 @@ """ Chart axes graphics and behavior. """ +import time +from functools import partial +from typing import List + + +# import numpy as np +import pandas as pd import pyqtgraph as pg from PyQt5 import QtCore, QtGui from PyQt5.QtCore import QPointF - -# from .quantdom.base import Quotes from .quantdom.utils import fromtimestamp from ._style import _font, hcolor @@ -15,7 +20,6 @@ class PriceAxis(pg.AxisItem): def __init__( self, - # chart: 'ChartPlotWidget', ) -> None: super().__init__(orientation='right') self.setStyle(**{ @@ -28,10 +32,7 @@ class PriceAxis(pg.AxisItem): }) self.setLabel(**{'font-size': '10pt'}) self.setTickFont(_font) - self.setWidth(150) - # self.chart = chart - # accesed normally via - # .getAxis('right') + self.setWidth(125) # XXX: drop for now since it just eats up h space @@ -43,9 +44,20 @@ class PriceAxis(pg.AxisItem): class DynamicDateAxis(pg.AxisItem): - tick_tpl = {'D1': '%Y-%b-%d'} + # time formats mapped by seconds between bars + tick_tpl = { + 60*60*24: '%Y-%b-%d', + 60: '%H:%M', + 30: '%H:%M:%S', + 5: '%H:%M:%S', + } - def __init__(self, linked_charts, *args, **kwargs): + def __init__( + self, + linked_charts, + *args, + **kwargs + ) -> None: super().__init__(*args, **kwargs) self.linked_charts = linked_charts self.setTickFont(_font) @@ -59,24 +71,25 @@ class DynamicDateAxis(pg.AxisItem): ) # self.setHeight(35) - def tickStrings(self, values, scale, spacing): - # if len(values) > 1 or not values: - # values = Quotes.time - - # strings = super().tickStrings(values, scale, spacing) - s_period = 'D1' - strings = [] + def _indexes_to_timestrs( + self, + indexes: List[int], + ) -> List[str]: bars = self.linked_charts.chart._array - quotes_count = len(bars) - 1 + times = bars['time'] + bars_len = len(bars) + delay = times[-1] - times[times != times[-1]][-1] - for ibar in values: - if ibar > quotes_count: - return strings - dt_tick = fromtimestamp(bars[int(ibar)]['time']) - strings.append( - dt_tick.strftime(self.tick_tpl[s_period]) - ) - return strings + epochs = times[list( + map(int, filter(lambda i: i < bars_len, indexes)) + )] + # TODO: **don't** have this hard coded shift to EST + dts = pd.to_datetime(epochs, unit='s') - 4*pd.offsets.Hour() + return dts.strftime(self.tick_tpl[delay]) + + + def tickStrings(self, values: List[float], scale, spacing): + return self._indexes_to_timestrs(values) class AxisLabel(pg.GraphicsObject): @@ -88,7 +101,7 @@ class AxisLabel(pg.GraphicsObject): def __init__( self, parent=None, - digits=1, + digits=2, color=None, opacity=1, **kwargs @@ -128,6 +141,7 @@ class AxisLabel(pg.GraphicsObject): p.drawText(option.rect, self.text_flags, self.label_str) # uggggghhhh + def tick_to_string(self, tick_pos): raise NotImplementedError() @@ -160,24 +174,20 @@ class XAxisLabel(AxisLabel): ) # text_flags = _common_text_flags - def tick_to_string(self, tick_pos): - # TODO: change to actual period - tpl = self.parent.tick_tpl['D1'] - bars = self.parent.linked_charts.chart._array - if tick_pos > len(bars): - return 'Unknown Time' - return fromtimestamp(bars[round(tick_pos)]['time']).strftime(tpl) - def boundingRect(self): # noqa - return QtCore.QRectF(0, 0, 145, 40) + # TODO: we need to get the parent axe's dimensions transformed + # to abs coords to be 100% correct here: + # self.parent.boundingRect() + return QtCore.QRectF(0, 0, 100, 31) - def update_label(self, abs_pos, data): - # ibar = view_pos.x() - # if ibar > self.quotes_count: - # return - self.label_str = self.tick_to_string(data) + def update_label( + self, + abs_pos: QPointF, # scene coords + data: float, # data for text + offset: int = 0 # if have margins, k? + ) -> None: + self.label_str = self.parent._indexes_to_timestrs([int(data)])[0] width = self.boundingRect().width() - offset = 0 # if have margins new_pos = QPointF(abs_pos.x() - width / 2 - offset, 0) self.setPos(new_pos) @@ -202,10 +212,10 @@ class YAxisLabel(AxisLabel): self, abs_pos: QPointF, # scene coords data: float, # data for text + offset: int = 0 # if have margins, k? ) -> None: self.label_str = self.tick_to_string(data) height = self.boundingRect().height() - offset = 0 # if have margins new_pos = QPointF(0, abs_pos.y() - height / 2 - offset) self.setPos(new_pos)