Merge pull request #413 from pikers/pg_exts_fork

Pg exts fork
no_signal_pi_overlays
goodboy 2022-11-08 12:47:32 -05:00 committed by GitHub
commit ba2e1e04cd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 528 additions and 52 deletions

View File

@ -18,7 +18,10 @@
Profiling wrappers for internal libs.
"""
import os
import sys
import time
from time import perf_counter
from functools import wraps
# NOTE: you can pass a flag to enable this:
@ -44,3 +47,184 @@ def timeit(fn):
return res
return wrapper
# Modified version of ``pyqtgraph.debug.Profiler`` that
# core seems hesitant to land in:
# https://github.com/pyqtgraph/pyqtgraph/pull/2281
class Profiler(object):
'''
Simple profiler allowing measurement of multiple time intervals.
By default, profilers are disabled. To enable profiling, set the
environment variable `PYQTGRAPHPROFILE` to a comma-separated list of
fully-qualified names of profiled functions.
Calling a profiler registers a message (defaulting to an increasing
counter) that contains the time elapsed since the last call. When the
profiler is about to be garbage-collected, the messages are passed to the
outer profiler if one is running, or printed to stdout otherwise.
If `delayed` is set to False, messages are immediately printed instead.
Example:
def function(...):
profiler = Profiler()
... do stuff ...
profiler('did stuff')
... do other stuff ...
profiler('did other stuff')
# profiler is garbage-collected and flushed at function end
If this function is a method of class C, setting `PYQTGRAPHPROFILE` to
"C.function" (without the module name) will enable this profiler.
For regular functions, use the qualified name of the function, stripping
only the initial "pyqtgraph." prefix from the module.
'''
_profilers = os.environ.get("PYQTGRAPHPROFILE", None)
_profilers = _profilers.split(",") if _profilers is not None else []
_depth = 0
# NOTE: without this defined at the class level
# you won't see apprpriately "nested" sub-profiler
# instance calls.
_msgs = []
# set this flag to disable all or individual profilers at runtime
disable = False
class DisabledProfiler(object):
def __init__(self, *args, **kwds):
pass
def __call__(self, *args):
pass
def finish(self):
pass
def mark(self, msg=None):
pass
_disabledProfiler = DisabledProfiler()
def __new__(
cls,
msg=None,
disabled='env',
delayed=True,
ms_threshold: float = 0.0,
):
"""Optionally create a new profiler based on caller's qualname.
``ms_threshold`` can be set to value in ms for which, if the
total measured time of the lifetime of this profiler is **less
than** this value, then no profiling messages will be printed.
Setting ``delayed=False`` disables this feature since messages
are emitted immediately.
"""
if (
disabled is True
or (
disabled == 'env'
and len(cls._profilers) == 0
)
):
return cls._disabledProfiler
# determine the qualified name of the caller function
caller_frame = sys._getframe(1)
try:
caller_object_type = type(caller_frame.f_locals["self"])
except KeyError: # we are in a regular function
qualifier = caller_frame.f_globals["__name__"].split(".", 1)[-1]
else: # we are in a method
qualifier = caller_object_type.__name__
func_qualname = qualifier + "." + caller_frame.f_code.co_name
if disabled == 'env' and func_qualname not in cls._profilers:
# don't do anything
return cls._disabledProfiler
# create an actual profiling object
cls._depth += 1
obj = super(Profiler, cls).__new__(cls)
obj._name = msg or func_qualname
obj._delayed = delayed
obj._markCount = 0
obj._finished = False
obj._firstTime = obj._lastTime = perf_counter()
obj._mt = ms_threshold
obj._newMsg("> Entering " + obj._name)
return obj
def __call__(self, msg=None):
"""Register or print a new message with timing information.
"""
if self.disable:
return
if msg is None:
msg = str(self._markCount)
self._markCount += 1
newTime = perf_counter()
ms = (newTime - self._lastTime) * 1000
self._newMsg(" %s: %0.4f ms", msg, ms)
self._lastTime = newTime
def mark(self, msg=None):
self(msg)
def _newMsg(self, msg, *args):
msg = " " * (self._depth - 1) + msg
if self._delayed:
self._msgs.append((msg, args))
else:
print(msg % args)
def __del__(self):
self.finish()
def finish(self, msg=None):
"""Add a final message; flush the message list if no parent profiler.
"""
if self._finished or self.disable:
return
self._finished = True
if msg is not None:
self(msg)
tot_ms = (perf_counter() - self._firstTime) * 1000
self._newMsg(
"< Exiting %s, total time: %0.4f ms",
self._name,
tot_ms,
)
if tot_ms < self._mt:
# print(f'{tot_ms} < {self._mt}, clearing')
# NOTE: this list **must** be an instance var to avoid
# deleting common messages during GC I think?
self._msgs.clear()
# else:
# print(f'{tot_ms} > {self._mt}, not clearing')
# XXX: why is this needed?
# don't we **want to show** nested profiler messages?
if self._msgs: # and self._depth < 1:
# if self._msgs:
print("\n".join([m[0] % m[1] for m in self._msgs]))
# clear all entries
self._msgs.clear()
# type(self)._msgs = []
type(self)._depth -= 1

View File

@ -56,6 +56,7 @@ if TYPE_CHECKING:
from .feed import maybe_open_feed
from ..log import get_logger, get_console_log
from .._profile import Profiler
log = get_logger(__name__)
@ -645,7 +646,7 @@ async def tsdb_history_update(
# * the original data feed arch blurb:
# - https://github.com/pikers/piker/issues/98
#
profiler = pg.debug.Profiler(
profiler = Profiler(
disabled=False, # not pg_profile_enabled(),
delayed=False,
)

View File

@ -44,6 +44,7 @@ from ._api import (
_load_builtins,
_Token,
)
from .._profile import Profiler
log = get_logger(__name__)
@ -91,7 +92,7 @@ async def fsp_compute(
) -> None:
profiler = pg.debug.Profiler(
profiler = Profiler(
delayed=False,
disabled=True
)
@ -262,7 +263,7 @@ async def cascade(
destination shm array buffer.
'''
profiler = pg.debug.Profiler(
profiler = Profiler(
delayed=False,
disabled=False
)

View File

@ -111,7 +111,8 @@ class LevelMarker(QGraphicsPathItem):
# get polygon and scale
super().__init__()
self.scale(size, size)
# self.setScale(size, size)
self.setScale(size)
# interally generates path
self._style = None

View File

@ -78,6 +78,8 @@ async def _async_main(
"""
from . import _display
from ._pg_overrides import _do_overrides
_do_overrides()
godwidget = main_widget

View File

@ -39,12 +39,17 @@ class Axis(pg.AxisItem):
'''
A better axis that sizes tick contents considering font size.
Also includes tick values lru caching originally proposed in but never
accepted upstream:
https://github.com/pyqtgraph/pyqtgraph/pull/2160
'''
def __init__(
self,
linkedsplits,
typical_max_str: str = '100 000.000',
text_color: str = 'bracket',
lru_cache_tick_strings: bool = True,
**kwargs
) -> None:
@ -91,6 +96,34 @@ class Axis(pg.AxisItem):
# size the pertinent axis dimension to a "typical value"
self.size_to_values()
# NOTE: requires override ``.tickValues()`` method seen below.
if lru_cache_tick_strings:
self.tickStrings = lru_cache(
maxsize=2**20
)(self.tickStrings)
# NOTE: only overriden to cast tick values entries into tuples
# for use with the lru caching.
def tickValues(
self,
minVal: float,
maxVal: float,
size: int,
) -> list[tuple[float, tuple[str]]]:
'''
Repack tick values into tuples for lru caching.
'''
ticks = []
for scalar, values in super().tickValues(minVal, maxVal, size):
ticks.append((
scalar,
tuple(values), # this
))
return ticks
@property
def text_color(self) -> str:
return self._text_color

View File

@ -73,6 +73,8 @@ from .._profile import pg_profile_enabled, ms_slower_then
from ._overlay import PlotItemOverlay
from ._flows import Flow
from ._search import SearchWidget
from . import _pg_overrides as pgo
from .._profile import Profiler
if TYPE_CHECKING:
from ._display import DisplayState
@ -831,6 +833,7 @@ class ChartPlotWidget(pg.PlotWidget):
static_yrange: Optional[tuple[float, float]] = None,
parent=None,
**kwargs,
):
'''
@ -848,12 +851,15 @@ class ChartPlotWidget(pg.PlotWidget):
# source of our custom interactions
self.cv = cv = self.mk_vb(name)
pi = pgo.PlotItem(viewBox=cv, **kwargs)
super().__init__(
background=hcolor(view_color),
viewBox=cv,
# parent=None,
# plotItem=None,
# antialias=True,
parent=parent,
plotItem=pi,
**kwargs
)
# give viewbox as reference to chart
@ -1144,7 +1150,7 @@ class ChartPlotWidget(pg.PlotWidget):
axis_side: str = 'right',
axis_kwargs: dict = {},
) -> pg.PlotItem:
) -> pgo.PlotItem:
# Custom viewbox impl
cv = self.mk_vb(name)
@ -1153,13 +1159,14 @@ class ChartPlotWidget(pg.PlotWidget):
allowed_sides = {'left', 'right'}
if axis_side not in allowed_sides:
raise ValueError(f'``axis_side``` must be in {allowed_sides}')
yaxis = PriceAxis(
orientation=axis_side,
linkedsplits=self.linked,
**axis_kwargs,
)
pi = pg.PlotItem(
pi = pgo.PlotItem(
parent=self.plotItem,
name=name,
enableMenu=False,
@ -1246,7 +1253,7 @@ class ChartPlotWidget(pg.PlotWidget):
# TODO: this probably needs its own method?
if overlay:
if isinstance(overlay, pg.PlotItem):
if isinstance(overlay, pgo.PlotItem):
if overlay not in self.pi_overlay.overlays:
raise RuntimeError(
f'{overlay} must be from `.plotitem_overlay()`'
@ -1405,7 +1412,7 @@ class ChartPlotWidget(pg.PlotWidget):
If ``bars_range`` is provided use that range.
'''
profiler = pg.debug.Profiler(
profiler = Profiler(
msg=f'`{str(self)}.maxmin(name={name})`: `{self.name}`',
disabled=not pg_profile_enabled(),
ms_threshold=ms_slower_then,

View File

@ -44,6 +44,7 @@ from ._style import hcolor
# ds_m4,
# )
from ..log import get_logger
from .._profile import Profiler
log = get_logger(__name__)
@ -331,7 +332,7 @@ class Curve(pg.GraphicsObject):
) -> None:
profiler = pg.debug.Profiler(
profiler = Profiler(
msg=f'Curve.paint(): `{self._name}`',
disabled=not pg_profile_enabled(),
ms_threshold=ms_slower_then,
@ -466,7 +467,7 @@ class StepCurve(Curve):
def sub_paint(
self,
p: QPainter,
profiler: pg.debug.Profiler,
profiler: Profiler,
) -> None:
# p.drawLines(*tuple(filter(bool, self._last_step_lines)))

View File

@ -66,6 +66,7 @@ from .._profile import (
ms_slower_then,
)
from ..log import get_logger
from .._profile import Profiler
log = get_logger(__name__)
@ -441,7 +442,7 @@ def graphics_update_cycle(
# TODO: just pass this as a direct ref to avoid so many attr accesses?
hist_chart = ds.godwidget.hist_linked.chart
profiler = pg.debug.Profiler(
profiler = Profiler(
msg=f'Graphics loop cycle for: `{chart.name}`',
delayed=True,
disabled=not pg_profile_enabled(),

View File

@ -26,7 +26,19 @@ from typing import (
)
import pyqtgraph as pg
from pyqtgraph import ViewBox, Point, QtCore, QtGui
from pyqtgraph import (
ViewBox,
Point,
QtCore,
QtWidgets,
)
from PyQt5.QtGui import (
QColor,
)
from PyQt5.QtWidgets import (
QLabel,
)
from pyqtgraph import functions as fn
from PyQt5.QtCore import QPointF
import numpy as np
@ -240,7 +252,7 @@ class LineEditor(Struct):
return lines
class SelectRect(QtGui.QGraphicsRectItem):
class SelectRect(QtWidgets.QGraphicsRectItem):
def __init__(
self,
@ -249,12 +261,12 @@ class SelectRect(QtGui.QGraphicsRectItem):
) -> None:
super().__init__(0, 0, 1, 1)
# self.rbScaleBox = QtGui.QGraphicsRectItem(0, 0, 1, 1)
# self.rbScaleBox = QGraphicsRectItem(0, 0, 1, 1)
self.vb = viewbox
self._chart: 'ChartPlotWidget' = None # noqa
# override selection box color
color = QtGui.QColor(hcolor(color))
color = QColor(hcolor(color))
self.setPen(fn.mkPen(color, width=1))
color.setAlpha(66)
self.setBrush(fn.mkBrush(color))
@ -262,7 +274,7 @@ class SelectRect(QtGui.QGraphicsRectItem):
self.hide()
self._label = None
label = self._label = QtGui.QLabel()
label = self._label = QLabel()
label.setTextFormat(0) # markdown
label.setFont(_font.font)
label.setMargin(0)
@ -299,8 +311,8 @@ class SelectRect(QtGui.QGraphicsRectItem):
# TODO: get bg color working
palette.setColor(
self._label.backgroundRole(),
# QtGui.QColor(chart.backgroundBrush()),
QtGui.QColor(hcolor('papas_special')),
# QColor(chart.backgroundBrush()),
QColor(hcolor('papas_special')),
)
def update_on_resize(self, vr, r):
@ -348,7 +360,7 @@ class SelectRect(QtGui.QGraphicsRectItem):
self.setPos(r.topLeft())
self.resetTransform()
self.scale(r.width(), r.height())
self.setRect(r)
self.show()
y1, y2 = start_pos.y(), end_pos.y()

View File

@ -20,19 +20,24 @@ Trio - Qt integration
Run ``trio`` in guest mode on top of the Qt event loop.
All global Qt runtime settings are mostly defined here.
"""
from __future__ import annotations
from typing import (
Callable,
Any,
Type,
TYPE_CHECKING,
)
import platform
import traceback
# Qt specific
import PyQt5 # noqa
from pyqtgraph import QtGui
from PyQt5.QtWidgets import (
QWidget,
QMainWindow,
QApplication,
)
from PyQt5 import QtCore
# from PyQt5.QtGui import QLabel, QStatusBar
from PyQt5.QtCore import (
pyqtRemoveInputHook,
Qt,
@ -49,6 +54,7 @@ from ..log import get_logger
from ._pg_overrides import _do_overrides
from . import _style
log = get_logger(__name__)
# pyqtgraph global config
@ -76,17 +82,17 @@ if platform.system() == "Windows":
def run_qtractor(
func: Callable,
args: tuple,
main_widget_type: Type[QtGui.QWidget],
main_widget_type: Type[QWidget],
tractor_kwargs: dict[str, Any] = {},
window_type: QtGui.QMainWindow = None,
window_type: QMainWindow = None,
) -> None:
# avoids annoying message when entering debugger from qt loop
pyqtRemoveInputHook()
app = QtGui.QApplication.instance()
app = QApplication.instance()
if app is None:
app = PyQt5.QtWidgets.QApplication([])
app = QApplication([])
# TODO: we might not need this if it's desired
# to cancel the tractor machinery on Qt loop

View File

@ -59,6 +59,7 @@ from ._curve import (
FlattenedOHLC,
)
from ..log import get_logger
from .._profile import Profiler
log = get_logger(__name__)
@ -130,7 +131,7 @@ def render_baritems(
int, int, np.ndarray,
int, int, np.ndarray,
],
profiler: pg.debug.Profiler,
profiler: Profiler,
**kwargs,
) -> None:
@ -517,7 +518,7 @@ class Flow(msgspec.Struct): # , frozen=True):
render: bool = True,
array_key: Optional[str] = None,
profiler: Optional[pg.debug.Profiler] = None,
profiler: Optional[Profiler] = None,
do_append: bool = True,
**kwargs,
@ -528,7 +529,7 @@ class Flow(msgspec.Struct): # , frozen=True):
render to graphics.
'''
profiler = pg.debug.Profiler(
profiler = Profiler(
msg=f'Flow.update_graphics() for {self.name}',
disabled=not pg_profile_enabled(),
ms_threshold=4,
@ -948,7 +949,7 @@ class Renderer(msgspec.Struct):
new_read,
array_key: str,
profiler: pg.debug.Profiler,
profiler: Profiler,
uppx: float = 1,
# redraw and ds flags

View File

@ -59,6 +59,7 @@ from ..fsp._volume import (
flow_rates,
)
from ..log import get_logger
from .._profile import Profiler
log = get_logger(__name__)
@ -190,7 +191,7 @@ async def open_fsp_actor_cluster(
from tractor._clustering import open_actor_cluster
# profiler = pg.debug.Profiler(
# profiler = Profiler(
# delayed=False,
# disabled=False
# )
@ -212,7 +213,7 @@ async def run_fsp_ui(
target: Fsp,
conf: dict[str, dict],
loglevel: str,
# profiler: pg.debug.Profiler,
# profiler: Profiler,
# _quote_throttle_rate: int = 58,
) -> None:
@ -746,6 +747,8 @@ async def open_vlm_displays(
},
)
dvlm_pi.hideAxis('left')
dvlm_pi.hideAxis('bottom')
# all to be overlayed curve names
fields = [
'dolla_vlm',
@ -878,6 +881,7 @@ async def open_vlm_displays(
# keep both regular and dark vlm in view
names=trade_rate_fields,
)
tr_pi.hideAxis('bottom')
chart_curves(
trade_rate_fields,
@ -951,7 +955,7 @@ async def start_fsp_displays(
# },
# },
}
profiler = pg.debug.Profiler(
profiler = Profiler(
delayed=False,
disabled=False
)

View File

@ -33,6 +33,7 @@ import numpy as np
import trio
from ..log import get_logger
from .._profile import Profiler
from .._profile import pg_profile_enabled, ms_slower_then
# from ._style import _min_points_to_show
from ._editors import SelectRect
@ -779,7 +780,7 @@ class ChartView(ViewBox):
'''
name = self.name
# print(f'YRANGE ON {name}')
profiler = pg.debug.Profiler(
profiler = Profiler(
msg=f'`ChartView._set_yrange()`: `{name}`',
disabled=not pg_profile_enabled(),
ms_threshold=ms_slower_then,
@ -916,7 +917,7 @@ class ChartView(ViewBox):
autoscale_overlays: bool = True,
):
profiler = pg.debug.Profiler(
profiler = Profiler(
msg=f'ChartView.maybe_downsample_graphics() for {self.name}',
disabled=not pg_profile_enabled(),

View File

@ -32,6 +32,7 @@ from PyQt5.QtGui import QPainterPath
from .._profile import pg_profile_enabled, ms_slower_then
from ._style import hcolor
from ..log import get_logger
from .._profile import Profiler
if TYPE_CHECKING:
from ._chart import LinkedSplits
@ -170,7 +171,7 @@ class BarItems(pg.GraphicsObject):
) -> None:
profiler = pg.debug.Profiler(
profiler = Profiler(
disabled=not pg_profile_enabled(),
ms_threshold=ms_slower_then,
)

View File

@ -15,11 +15,15 @@
# along with this program. If not, see <https://www.gnu.org/licenses/>.
"""
Customization of ``pyqtgraph`` core routines to speed up our use mostly
based on not requiring "scentific precision" for pixel perfect view
transforms.
Customization of ``pyqtgraph`` core routines and various types normally
for speedups.
Generally, our does not require "scentific precision" for pixel perfect
view transforms.
"""
from typing import Optional
import pyqtgraph as pg
@ -46,3 +50,211 @@ def _do_overrides() -> None:
"""
# we don't care about potential fp issues inside Qt
pg.functions.invertQTransform = invertQTransform
pg.PlotItem = PlotItem
# NOTE: the below customized type contains all our changes on a method
# by method basis as per the diff:
# https://github.com/pyqtgraph/pyqtgraph/commit/8e60bc14234b6bec1369ff4192dbfb82f8682920#diff-a2b5865955d2ba703dbc4c35ff01aa761aa28d2aeaac5e68d24e338bc82fb5b1R500
class PlotItem(pg.PlotItem):
'''
Overrides for the core plot object mostly pertaining to overlayed
multi-view management as it relates to multi-axis managment.
'''
def __init__(
self,
parent=None,
name=None,
labels=None,
title=None,
viewBox=None,
axisItems=None,
default_axes=['left', 'bottom'],
enableMenu=True,
**kargs
):
super().__init__(
parent=parent,
name=name,
labels=labels,
title=title,
viewBox=viewBox,
axisItems=axisItems,
# default_axes=default_axes,
enableMenu=enableMenu,
kargs=kargs,
)
# self.setAxisItems(
# axisItems,
# default_axes=default_axes,
# )
# NOTE: this is an entirely new method not in upstream.
def removeAxis(
self,
name: str,
unlink: bool = True,
) -> Optional[pg.AxisItem]:
"""
Remove an axis from the contained axis items
by ```name: str```.
This means the axis graphics object will be removed
from the ``.layout: QGraphicsGridLayout`` as well as unlinked
from the underlying associated ``ViewBox``.
If the ``unlink: bool`` is set to ``False`` then the axis will
stay linked to its view and will only be removed from the
layoutonly be removed from the layout.
If no axis with ``name: str`` is found then this is a noop.
Return the axis instance that was removed.
"""
entry = self.axes.pop(name, None)
if not entry:
return
axis = entry['item']
self.layout.removeItem(axis)
axis.scene().removeItem(axis)
if unlink:
axis.unlinkFromView()
self.update()
return axis
# Why do we need to always have all axes created?
#
# I don't understand this at all.
#
# Everything seems to work if you just always apply the
# set passed to this method **EXCEPT** for some super weird reason
# the view box geometry still computes as though the space for the
# `'bottom'` axis is always there **UNLESS** you always add that
# axis but hide it?
#
# Why in tf would this be the case!?!?
def setAxisItems(
self,
# XXX: yeah yeah, i know we can't use type annots like this yet.
axisItems: Optional[dict[str, pg.AxisItem]] = None,
add_to_layout: bool = True,
default_axes: list[str] = ['left', 'bottom'],
):
"""
Override axis item setting to only
"""
axisItems = axisItems or {}
# XXX: wth is is this even saying?!?
# Array containing visible axis items
# Also containing potentially hidden axes, but they are not
# touched so it does not matter
# visibleAxes = ['left', 'bottom']
# Note that it does not matter that this adds
# some values to visibleAxes a second time
# XXX: uhhh wat^ ..?
visibleAxes = list(default_axes) + list(axisItems.keys())
# TODO: we should probably invert the loop here to not loop the
# predefined "axis name set" and instead loop the `axisItems`
# input and lookup indices from a predefined map.
for name, pos in (
('top', (1, 1)),
('bottom', (3, 1)),
('left', (2, 0)),
('right', (2, 2))
):
if (
name in self.axes and
name in axisItems
):
# we already have an axis entry for this name
# so remove the existing entry.
self.removeAxis(name)
# elif name not in axisItems:
# # this axis entry is not provided in this call
# # so remove any old/existing entry.
# self.removeAxis(name)
# Create new axis
if name in axisItems:
axis = axisItems[name]
if axis.scene() is not None:
if (
name not in self.axes
or axis != self.axes[name]["item"]
):
raise RuntimeError(
"Can't add an axis to multiple plots. Shared axes"
" can be achieved with multiple AxisItem instances"
" and set[X/Y]Link.")
else:
# Set up new axis
# XXX: ok but why do we want to add axes for all entries
# if not desired by the user? The only reason I can see
# adding this is without it there's some weird
# ``ViewBox`` geometry bug.. where a gap for the
# 'bottom' axis is somehow left in?
axis = pg.AxisItem(orientation=name, parent=self)
axis.linkToView(self.vb)
# XXX: shouldn't you already know the ``pos`` from the name?
# Oh right instead of a global map that would let you
# reasily look that up it's redefined over and over and over
# again in methods..
self.axes[name] = {'item': axis, 'pos': pos}
# NOTE: in the overlay case the axis may be added to some
# other layout and should not be added here.
if add_to_layout:
self.layout.addItem(axis, *pos)
# place axis above images at z=0, items that want to draw
# over the axes should be placed at z>=1:
axis.setZValue(0.5)
axis.setFlag(
axis.GraphicsItemFlag.ItemNegativeZStacksBehindParent
)
if name in visibleAxes:
self.showAxis(name, True)
else:
# why do we need to insert all axes to ``.axes`` and
# only hide the ones the user doesn't specify? It all
# seems to work fine without doing this except for this
# weird gap for the 'bottom' axis that always shows up
# in the view box geometry??
self.hideAxis(name)
def updateGrid(
self,
*args,
):
alpha = self.ctrl.gridAlphaSlider.value()
x = alpha if self.ctrl.xGridCheck.isChecked() else False
y = alpha if self.ctrl.yGridCheck.isChecked() else False
for name, dim in (
('top', x),
('bottom', x),
('left', y),
('right', y)
):
if name in self.axes:
self.getAxis(name).setGrid(dim)
# self.getAxis('bottom').setGrid(x)
# self.getAxis('left').setGrid(y)
# self.getAxis('right').setGrid(y)

View File

@ -28,10 +28,19 @@ from typing import (
)
import uuid
from pyqtgraph import QtGui
from PyQt5 import QtCore
from PyQt5.QtWidgets import QLabel, QStatusBar
from PyQt5.QtWidgets import (
QWidget,
QMainWindow,
QApplication,
QLabel,
QStatusBar,
)
from PyQt5.QtGui import (
QScreen,
QCloseEvent,
)
from ..log import get_logger
from ._style import _font_small, hcolor
from ._chart import GodWidget
@ -153,7 +162,7 @@ class MultiStatus:
self.bar.clearMessage()
class MainWindow(QtGui.QMainWindow):
class MainWindow(QMainWindow):
# XXX: for tiling wms this should scale
# with the alloted window size.
@ -176,12 +185,12 @@ class MainWindow(QtGui.QMainWindow):
self._size: Optional[tuple[int, int]] = None
@property
def mode_label(self) -> QtGui.QLabel:
def mode_label(self) -> QLabel:
# init mode label
if not self._status_label:
self._status_label = label = QtGui.QLabel()
self._status_label = label = QLabel()
label.setStyleSheet(
f"""QLabel {{
color : {hcolor('gunmetal')};
@ -203,8 +212,7 @@ class MainWindow(QtGui.QMainWindow):
def closeEvent(
self,
event: QtGui.QCloseEvent,
event: QCloseEvent,
) -> None:
'''Cancel the root actor asap.
@ -244,8 +252,8 @@ class MainWindow(QtGui.QMainWindow):
def on_focus_change(
self,
last: QtGui.QWidget,
current: QtGui.QWidget,
last: QWidget,
current: QWidget,
) -> None:
@ -256,12 +264,12 @@ class MainWindow(QtGui.QMainWindow):
name = getattr(current, 'mode_name', '')
self.set_mode_name(name)
def current_screen(self) -> QtGui.QScreen:
def current_screen(self) -> QScreen:
'''
Get a frickin screen (if we can, gawd).
'''
app = QtGui.QApplication.instance()
app = QApplication.instance()
for _ in range(3):
screen = app.screenAt(self.pos())
@ -294,7 +302,7 @@ class MainWindow(QtGui.QMainWindow):
'''
# https://stackoverflow.com/a/18975846
if not size and not self._size:
# app = QtGui.QApplication.instance()
# app = QApplication.instance()
geo = self.current_screen().geometry()
h, w = geo.height(), geo.width()
# use approx 1/3 of the area of the screen by default
@ -331,7 +339,7 @@ class MainWindow(QtGui.QMainWindow):
# singleton app per actor
_qt_win: QtGui.QMainWindow = None
_qt_win: QMainWindow = None
def main_window() -> MainWindow:

View File

@ -6,7 +6,7 @@
# `pyqtgraph` peeps keep breaking, fixing, improving so might as well
# pin this to a dev branch that we have more control over especially
# as more graphics stuff gets hashed out.
-e git+https://github.com/pikers/pyqtgraph.git@piker_pin#egg=pyqtgraph
-e git+https://github.com/pikers/pyqtgraph.git@master#egg=pyqtgraph
# our async client for ``marketstore`` (the tsdb)
-e git+https://github.com/pikers/anyio-marketstore.git@master#egg=anyio-marketstore