Reputation: 199
I have a QChartView
with QChart. I've added QRubberBand
to the QChartView
to zoom the chart. Plus I overrode wheelEvent
, to scroll the chart by X axis.
So, now I want to prevent 'outscrolling' and 'outzooming' over some value. By this names I mean, that I need to stop zoom out, when chart size reaches the original chart size (so, minimal size of the chart should be it's original size) and I need to stop scrolling event, when I reach the min or max chart value (for the same reason).
I've tried to use size of the QChart
and QChartView
to handle QRubberBand
resizing as beginning, but it didn't help.
Here is my small try, but I can't find out any compatible solution for QWheelEvent
and QMouseEvent
(but may be there is any build in methods for these tasks, so I should not override that two events). Here is the code:
import sys
from PySide6.QtCore import Qt
from PySide6.QtWidgets import QApplication, QMainWindow, QVBoxLayout, QWidget
from PySide6 import QtCharts, QtGui
import random
class Chart(QtCharts.QChart):
def __init__(self):
super().__init__()
def wheelEvent(self, event):
print(event.delta())
if event.delta() > 0:
self.scroll(-10, 0)
print('e')
else:
self.scroll(10, 0)
def create_series(self):
for _ in range(2):
series = QtCharts.QLineSeries()
for i in range(100):
series.append(i, random.randint(0, 10))
self.addSeries(series)
def setup(self):
self.createDefaultAxes()
self.legend().setVisible(True)
self.legend().setAlignment(Qt.AlignLeft)
class ChartView(QtCharts.QChartView):
def __init__(self, chart):
super().__init__()
self.setChart(chart)
self.setRenderHint(QtGui.QPainter.Antialiasing)
self.setRubberBand(QtCharts.QChartView.HorizontalRubberBand)
def mousePressEvent(self, event):
if event.button() == Qt.RightButton:
if self.chart().size().width() > self.size().width():
print("Fit")
super().mousePressEvent(event)
else:
print(self, "Doesn't fit")
print(self.chart().size().width())
print(self.size().width())
else:
super().mousePressEvent(event)
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
self.layout = QVBoxLayout()
for _ in range(2):
chart_view = self.create_chart_view()
self.layout.addWidget(chart_view)
self.central_widget = QWidget()
self.central_widget.setLayout(self.layout)
self.setCentralWidget(self.central_widget)
@staticmethod
def create_chart():
chart = Chart()
chart.create_series()
chart.setup()
return chart
def create_chart_view(self):
chart_view = ChartView(chart=self.create_chart())
chart_view.chart().axes(Qt.Horizontal)[0].rangeChanged.connect(
lambda: (self.sync_chart_views(chart_view))
)
return chart_view
def sync_chart_views(self, source_chart_view):
x_range = (source_chart_view.chart().axes(Qt.Horizontal)[0].min(), source_chart_view.chart().axes(Qt.Horizontal)[0].max())
for chart_view in self.findChildren(QtCharts.QChartView):
if chart_view != source_chart_view:
chart_view.chart().axes(Qt.Horizontal)[0].setRange(x_range[0], x_range[1])
if __name__ == "__main__":
app = QApplication(sys.argv)
window = MainWindow()
window.show()
sys.exit(app.exec())
Upvotes: 0
Views: 310
Reputation: 199
Here is the solution. The main thing is to call super().mouseReleaseEvent(event)
before chart axes values setup.
import sys
from PySide6.QtCore import Qt
from PySide6.QtWidgets import QApplication, QMainWindow, QVBoxLayout, QWidget, QToolTip
from PySide6 import QtCharts, QtGui
import random
class Chart(QtCharts.QChart):
def __init__(self):
super().__init__()
self.create_series()
self.createDefaultAxes()
self.legend().setVisible(True)
self.legend().setAlignment(Qt.AlignLeft)
self.x_min_value = self.axes(Qt.Horizontal)[0].min()
self.x_max_value = self.axes(Qt.Horizontal)[0].max()
self.y_min_value = self.axes(Qt.Vertical)[0].min()
self.y_max_value = self.axes(Qt.Vertical)[0].max()
self.layout().setContentsMargins(0, 0, 0, 0)
def create_series(self, s=3):
for j in range(s):
series = QtCharts.QLineSeries()
if j == 0:
for i in range(150):
if i % 10 == 0:
series.append(i, random.randint(0, 10))
else:
series.append(i, 2)
self.addSeries(series)
def wheelEvent(self, event):
print(event.delta())
if event.delta() > 0 and self.axes(Qt.Horizontal)[0].min() > self.x_min_value:
self.scroll(-10, 0)
print('e')
elif event.delta() < 0 and self.axes(Qt.Horizontal)[0].max() < self.x_max_value:
self.scroll(10, 0)
class ChartView(QtCharts.QChartView):
def __init__(self, chart):
super().__init__()
self.setChart(chart)
[series.hovered.connect(self.display_plot_value) for series in chart.series()]
self.setRenderHint(QtGui.QPainter.Antialiasing)
self.setRubberBand(QtCharts.QChartView.HorizontalRubberBand)
self.x_min_value = chart.x_min_value
self.x_max_value = chart.x_max_value
self.y_min_value = chart.y_min_value
self.y_max_value = chart.y_max_value
@staticmethod
def display_plot_value(point, state):
pos = QtGui.QCursor.pos()
if state:
tooltip_text = f"X: {round(point.x(), 3)}, Y: {round(point.y(), 3)}"
QToolTip.showText(pos, tooltip_text)
else:
QToolTip.hideText()
def mouseReleaseEvent(self, event) -> None:
super().mouseReleaseEvent(event)
if event.button() == Qt.RightButton:
if self.chart().axes(Qt.Horizontal)[0].min() < self.x_min_value:
self.chart().axes(Qt.Horizontal)[0].setRange(self.x_min_value, self.chart().axes(Qt.Horizontal)[0].max())
if self.chart().axes(Qt.Horizontal)[0].max() > self.x_max_value:
self.chart().axes(Qt.Horizontal)[0].setRange(self.chart().axes(Qt.Horizontal)[0].min(), self.x_max_value)
if self.chart().axes(Qt.Vertical)[0].min() < self.y_min_value:
self.chart().axes(Qt.Vertical)[0].setRange(self.y_min_value, self.chart().axes(Qt.Vertical)[0].max())
if self.chart().axes(Qt.Vertical)[0].max() > self.y_max_value:
self.chart().axes(Qt.Vertical)[0].setRange(self.chart().axes(Qt.Vertical)[0].min(), self.y_max_value)
return
elif event.button() == Qt.LeftButton:
if self.chart().mapToValue(event.position()).x() > self.x_max_value:
self.chart().axes(Qt.Horizontal)[0].setMax(self.x_max_value)
if self.chart().mapToValue(event.position()).x() < self.x_min_value:
self.chart().axes(Qt.Horizontal)[0].setMin(self.x_min_value)
if self.chart().mapToValue(event.position()).y() > self.y_max_value:
self.chart().axes(Qt.Vertical)[0].setMax(self.y_max_value)
if self.chart().mapToValue(event.position()).y() < self.y_min_value:
self.chart().axes(Qt.Vertical)[0].setMin(self.y_min_value)
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
self.central_widget = QWidget()
self.central_widget.setObjectName('central_widget')
self.layout = QVBoxLayout(self.central_widget)
self.layout.setSpacing(0)
self.layout.setContentsMargins(0, 0, 0, 0)
for _ in range(6):
chart_view = self.create_chart_view()
self.layout.addWidget(chart_view)
self.setCentralWidget(self.central_widget)
def create_chart_view(self):
chart = Chart()
chart_view = ChartView(chart=chart)
return chart_view
if __name__ == "__main__":
app = QApplication(sys.argv)
window = MainWindow()
window.show()
sys.exit(app.exec())
Upvotes: 0