I want to have the auto-scale button that is normally in the bottom left hand corner and OUTSIDE of each subplot for a pyqtgraph, but rather than auto-scaling to fit the data, scale to a specific coordinate. The issue is that if I add a button to each subplot, it uses the location x and y axis scale. I want it so that it is always in the bottom left hand corner of each subplot no matter the values (like the default A button). Affectively, I want the auto-scale button to show even more area on the x and y axes than all the data points
I tried using:
auto_button = QPushButton(f"Auto {i+1}")
auto_button.setFixedSize(20, 10)
auto_button.clicked.connect(lambda _, p=plot, xmin=self.xdata[i][0], xmax=self.xdata[i][-1],ymin=self.ylim[i][0], ymax=self.ylim[i][1]: self.set_custom_range(p, xmin, xmax, ymin, ymax))
proxy = QtWidgets.QGraphicsProxyWidget()
proxy.setWidget(auto_button)
proxy.setPos(10,10)
plot.addItem(proxy)
but the size and position of that button will be local to the x axis and INSIDE the plot.
Is there a way I can have it so that it is exactly where the 'A' button is?
My code:
import sys
import random
import time
import multiprocessing
import numpy as np
from PyQt5 import QtWidgets, QtCore
import pyqtgraph as pg
class StockPriceUpdater(multiprocessing.Process):
def __init__(self, pipe, stock_name, prices):
super().__init__()
self.pipe = pipe
self.stock_name = stock_name
self.prices = prices
def run(self):
while True:
new_price = self.prices[-1] + random.uniform(-0.5, 0.5)
self.prices = np.append(self.prices, new_price)
self.pipe.send((self.stock_name, self.prices.copy()))
time.sleep(10)
def autoBtnClicked2(self):
print('We have a click')
if self.autoBtn.mode == 'auto':
print('auto_button true')
# Changed this : self.enableAutoRange()
x, y = self.curves[0].getOriginalDataset()
self.vb.setRange(
xRange=[x[0]-20, 2*x[-1]],
yRange=[y[0], y[-1]]
)
# End of change
self.autoBtn.hide()
else:
print('auto_button false')
self.disableAutoRange()
class StockPriceApp(QtWidgets.QMainWindow):
def __init__(self):
super().__init__()
self.initUI()
self.setup_multiprocessing()
def initUI(self):
self.setWindowTitle('Stock Prices')
central_widget = QtWidgets.QWidget()
self.setCentralWidget(central_widget)
layout = QtWidgets.QVBoxLayout(central_widget)
self.graph_layout = pg.GraphicsLayoutWidget()
layout.addWidget(self.graph_layout)
self.plots = []
self.curves = []
self.xdata = [np.arange(10)] * 4
self.ylim = {}
self.xlim = {}
self.ini_ydata = {}
for i in range(4):
plot = self.graph_layout.addPlot(row=i//2, col=i%2, title=f'Stock {i+1}')
prices = 10 + np.cumsum(np.random.uniform(-0.5, 0.5, 10))
plot.autoBtnClicked = autoBtnClicked2 # The adjustment
self.update_axis_limits(plot, i, self.xdata[i], prices)
self.ini_ydata[i] = prices
curve = plot.plot(self.xdata[i], prices, pen='y')
self.plots.append(plot)
self.curves.append(curve)
self.timer = QtCore.QTimer()
self.timer.timeout.connect(self.update_plots)
self.timer.start(1000)
def position_button(self, view_box, proxy):
rect = view_box.sceneBoundingRect()
proxy.setPos(rect.left() + 10, rect.bottom() - 40)
def setup_multiprocessing(self):
self.pipes = {}
self.processes = {}
for i in range(4):
stock_name = f'Stock{i+1}'
parent_pipe, child_pipe = multiprocessing.Pipe()
self.pipes[stock_name] = parent_pipe
process = StockPriceUpdater(child_pipe, stock_name, self.ini_ydata[i])
process.start()
self.processes[stock_name] = process
def update_plots(self):
for i, (stock_name, pipe) in enumerate(self.pipes.items()):
if pipe.poll():
_, prices = pipe.recv()
self.xdata[i] = np.append(self.xdata[i], self.xdata[i][-1] + 1)
self.curves[i].setData(self.xdata[i], prices)
self.update_axis_limits(self.plots[i], i, self.xdata[i], prices)
def set_custom_range(self, plot, x_min, x_max, y_min, y_max):
plot.getViewBox().setRange(xRange=[x_min, x_max], yRange=[y_min, y_max])
def update_axis_limits(self, plot, stock, xvals, yvals):
xmin = xvals.min()
xmax = xvals.max()
offset = (xmax - xmin) / 3
xmax = xmax + offset
if isinstance(yvals, list):
yvals = np.array(yvals)
ymin = yvals.min()
ymax = yvals.max()
offset = (ymax - ymin) / 10
ymax = ymax + offset
ymin = ymin - offset
self.ylim[stock] = (ymin, ymax)
self.xlim[stock] = (xmin, xmax)
plot.getViewBox().setRange(xRange=[xmin, xmax], yRange=[ymin, ymax], padding=0)
def closeEvent(self, event):
for process in self.processes.values():
process.terminate()
event.accept()
def main():
app = QtWidgets.QApplication(sys.argv)
stock_app = StockPriceApp()
stock_app.show()
sys.exit(app.exec_())
if __name__ == '__main__':
main()