Как добавить перекрестие на график pyqt5
Мне нужно добавить перекрестие, как здесь, на странице. crosshair Перекрестие должно перемещаться за курсором мыши. В Интернете есть несколько примеров по этой теме. Повсюду есть "pyqtgraph". Я их не понимаю. Я хочу использовать кнопку, чтобы добавлять и удалять перекрестие. Могу ли я использовать только один pyqt?
Можно использовать этот код как основу для добавления прицела.
from PyQt5 import QtCore, QtGui, QtWidgets, QtChart
from PyQt5.QtCore import *
from PyQt5.QtChart import *
import math
import numpy as np
mas =[1.33, 1.15, 1.55, 1.65, 1.64, 1.91, 1.33, 2.3, 1.5, 1.35, 2.52, 1.77, 1.7, 1.87, 2.0, 1.55, 1.73, 2.1,
1.33, 1.15, 1.55, 1.92, 1.64, 1.91, 1.33, 1.71, 1.5, 1.35, 1.22, 1.77, 1.7, 1.87, 2.7, 1.55, 1.73, 2.1,
1.33, 1.15, 1.55, 1.92, 1.64, 1.91, 1.33, 1.71, 1.5, 1.35, 1.22, 1.77, 1.7, 1.87, 2.0, 1.55, 1.73, 2.1]
x = len(mas)
x_ = x - 1
class MainWindow(QtWidgets.QMainWindow):
def __init__(self, start = 1, parent=None):
self.start = start
super().__init__(parent)
self.step = 30
self._chart_view = QtChart.QChartView()
self.scrollbar = QtWidgets.QScrollBar(
QtCore.Qt.Horizontal,
sliderMoved=self.onAxisSliderMoved,
pageStep=self.step,
)
self.scrollbar.setRange(0, x_)
central_widget = QtWidgets.QWidget()
self.setCentralWidget(central_widget)
lay = QtWidgets.QVBoxLayout(central_widget)
for w in (self._chart_view, self.scrollbar):
lay.addWidget(w)
self._chart = QtChart.QChart()
self._line_serie = QtChart.QLineSeries()
for i in range(0, len(mas)):
self._line_serie.append(QtCore.QPointF(i, mas[i]))
min_x, max_x = 0, x_
self._chart.addSeries(self._line_serie)
axisX = QValueAxis()
axisX.setTickCount(5)
axisX.setLabelFormat("%d")
self._chart.addAxis(axisX, Qt.AlignBottom)
self._line_serie.attachAxis(axisX)
axisY = QValueAxis()
self._chart.addAxis(axisY, Qt.AlignLeft)
self._line_serie.attachAxis(axisY)
self._chart.legend().hide()
self._chart_view.setChart(self._chart)
self.lims = np.array([min_x, max_x])
self.onAxisSliderMoved(self.scrollbar.value())
self.adjust_axes(self.start, 31)
def adjust_axes(self, value_min, value_max):
if value_min >= 0 and value_max >= 0 and value_max <= x_ and value_max > value_min:
self._chart.axisX(self._line_serie).setRange(value_min, value_max)
@QtCore.pyqtSlot(int)
def onAxisSliderMoved(self, value):
value2 = value + self.step
value1 = value
if value2 >= x_:
value2 = x_
value1 = value2 - self.step
self.adjust_axes(math.floor(value1), math.ceil(value2))
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
w = MainWindow(start = 0)
w.show()
sys.exit(app.exec_())
2 ответа
Решение
Возможное решение - переопределить метод drawForeground QChartView, в котором линии должны рисоваться в зависимости от положения мыши:
class ChartView(QtChart.QChartView):
def __init__(self, parent=None):
super().__init__(parent)
self._value_pos = QtCore.QPoint()
self.setMouseTracking(True)
def drawForeground(self, painter, rect):
super().drawForeground(painter, rect)
if self.chart() is None or self._value_pos.isNull():
return
pen = QtGui.QPen(QtGui.QColor("salmon"))
pen.setWidth(8)
painter.setPen(pen)
area = self.chart().plotArea()
sp = self.chart().mapToPosition(self._value_pos)
x1 = QtCore.QPointF(area.left() + pen.width() / 2, sp.y())
x2 = QtCore.QPointF(area.right() - pen.width() / 2, sp.y())
y1 = QtCore.QPointF(sp.x(), area.top() + pen.width() / 2)
y2 = QtCore.QPointF(sp.x(), area.bottom() - pen.width() / 2)
if area.left() <= sp.x() <= area.right():
painter.drawLine(y1, y2)
if area.top() < sp.y() < area.bottom():
painter.drawLine(x1, x2)
def mouseMoveEvent(self, event):
super().mouseMoveEvent(event)
if self.chart() is None:
return
sp = self.mapToScene(event.pos())
if self.chart().plotArea().contains(sp):
self._value_pos = self.chart().mapToValue(sp)
self.setCursor(QtCore.Qt.PointingHandCursor)
else:
self.unsetCursor()
self.update()
class MainWindow(QtWidgets.QMainWindow):
def __init__(self, start=1, parent=None):
super().__init__(parent)
self.start = start
self._chart_view = ChartView()
# ...
Я попробовал вышеуказанное решение и не был доволен производительностью на динамическом графике. здесь лучший подход, который позаботился об этом.
class Crosshairs():
def __init__(self, chart: QChart, scene: QGraphicsScene):
self.m_xLine = QGraphicsLineItem()
self.m_yLine = QGraphicsLineItem()
self.m_xText = QGraphicsTextItem()
self.m_yText = QGraphicsTextItem()
self.m_chart = chart
self.m_xLine.setPen(QPen('red'))
self.m_yLine.setPen(QPen('red'))
self.m_xText.setZValue(11)
self.m_yText.setZValue(11)
self.m_xText.document().setDocumentMargin(0.0)
self.m_yText.document().setDocumentMargin(0.0)
self.m_xText.setDefaultTextColor('white')
self.m_yText.setDefaultTextColor('white')
# add lines and text to scene
scene.addItem(self.m_xLine)
scene.addItem(self.m_yLine)
scene.addItem(self.m_xText)
scene.addItem(self.m_yText)
def updatePostion(self, position):
#print(f'updating to : {position} for {self.m_chart}')
xLine = QLineF(position.x(), self.m_chart.plotArea().top(), position.x(), self.m_chart.plotArea().bottom())
yLine = QLineF(self.m_chart.plotArea().left(), position.y(), self.m_chart.plotArea().right(), position.y())
self.m_xLine.setLine(xLine)
self.m_yLine.setLine(yLine)
xText = f'{self.m_chart.mapToValue(position).x()}'
yText = f'{self.m_chart.mapToValue(position).y()}'
self.m_xText.setHtml(f"<div style='background-color: #ff0000;'> {xText} </div>")
self.m_yText.setHtml(f"<div style='background-color: #ff0000;'> {yText} </div>")
self.m_xText.setPos(position.x() - self.m_xText.boundingRect().width() / 2.0, self.m_chart.plotArea().bottom())
self.m_yText.setPos(self.m_chart.plotArea().right(), position.y() - self.m_yText.boundingRect().height() / 2.0)
if (not(self.m_chart.plotArea().contains(position))):
self.m_xLine.hide()
self.m_xText.hide()
self.m_yLine.hide()
self.m_yText.hide()
else:
self.m_xLine.show()
self.m_xText.show()
self.m_yLine.show()
self.m_yText.show()
class ChartView(QChartView):
def __init__(self, chart: QChart):
super(ChartView, self).__init__(chart)
self.crosshair = Crosshairs(chart, self.scene())
def mouseMoveEvent(self, event) -> None:
self.crosshair.updatePostion(event.scenePosition())
if __name__ == "__main__":
a = QApplication(sys.argv)
window = QMainWindow()
chart = Chart()
chart.setTitle("Dynamic spline chart")
chart.setAnimationOptions(QChart.AllAnimations)
chart_view = ChartView(chart)
chart_view.setRenderHint(QPainter.Antialiasing)
# for item in chart_view.items():
# print(f'{item.isWidget()} | {item.type()} :: {item}')
window.setCentralWidget(chart_view)
window.resize(400, 300)
window.show()
sys.exit(a.exec())