Tom McLean
Tom McLean

Reputation: 6295

How do I prevent pyqtgraph from making the axis larger than the widget size?

I have a pyqt5 gui which has two pyqtgraphs inside. The left graph will plot the speed of some object, so will need a dynamically changing y axis and the right plot will show the position, which will be inside a box which is 61m x 121m. Currently, the graphs are plotting but the axis are overlapping the edges and cutting off the axis/numbers. In addition, I have added a title to the plots but they arent showing either. How do I fit the plot correctly?

enter image description here

Here is my code:

from PyQt5 import QtWidgets, uic, QtCore
from pyqtgraph import PlotWidget
import pyqtgraph as pg
import sys
import numpy as np

class MainWindow(QtWidgets.QMainWindow):

    def __init__(self, *args, **kwargs):
        super(MainWindow, self).__init__(*args, **kwargs)

        #Load the UI Page
        uic.loadUi('mainwindow.ui', self)
        
        self.position.setXRange(0,60)
        self.position.setYRange(0,120)
        self.position.setWindowTitle("Position")

    def plot(self):
        x = np.random.rand(100)
        y = np.random.rand(100)
        self.position.plot(x, y)

def main():
    app = QtWidgets.QApplication(sys.argv)
    main = MainWindow()
    main.show()
    sys.exit(app.exec_())

if __name__ == '__main__':         
    main()

and my mainwindow.ui file is:

<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
 <class>MainWindow</class>
 <widget class="QMainWindow" name="MainWindow">
  <property name="geometry">
   <rect>
    <x>0</x>
    <y>0</y>
    <width>791</width>
    <height>597</height>
   </rect>
  </property>
  <property name="windowTitle">
   <string>MainWindow</string>
  </property>
  <property name="styleSheet">
   <string notr="true">background-color: black;</string>
  </property>
  <widget class="QWidget" name="centralwidget">
   <widget class="QWidget" name="horizontalLayoutWidget">
    <property name="geometry">
     <rect>
      <x>10</x>
      <y>110</y>
      <width>771</width>
      <height>451</height>
     </rect>
    </property>
    <layout class="QHBoxLayout" name="horizontalLayout">
     <property name="spacing">
      <number>16</number>
     </property>
     <property name="leftMargin">
      <number>10</number>
     </property>
     <property name="topMargin">
      <number>10</number>
     </property>
     <property name="rightMargin">
      <number>10</number>
     </property>
     <property name="bottomMargin">
      <number>10</number>
     </property>
     <item>
      <widget class="PlotWidget" name="speed" native="true"/>
     </item>
     <item>
      <widget class="PlotWidget" name="position" native="true"/>
     </item>
    </layout>
   </widget>
  </widget>
  <widget class="QMenuBar" name="menubar">
   <property name="geometry">
    <rect>
     <x>0</x>
     <y>0</y>
     <width>791</width>
     <height>21</height>
    </rect>
   </property>
  </widget>
  <widget class="QStatusBar" name="statusbar"/>
 </widget>
 <customwidgets>
  <customwidget>
   <class>PlotWidget</class>
   <extends>QWidget</extends>
   <header>pyqtgraph</header>
   <container>1</container>
  </customwidget>
 </customwidgets>
 <resources/>
 <connections/>
</ui>

Upvotes: 1

Views: 2662

Answers (2)

Olger Siebinga
Olger Siebinga

Reputation: 21

To prevent the overlapping axis you could make use of the fact that the PlotWidget inherits from QGraphicsView. QGraphicsView has a fitInView() method that can be used to fit a rectangle in the current view.

The method sceneRect() returns a rectangle that describes "the area of the scene visualized by this view". By slightly growing this rectangle you can add some padding. This is done in the example below in the _add_padding_to_plot_widget() method. Please note that this only works after the QT app is started, that's why the function gets called from showEvent. Directly calling it from __init__ does not work.

example code:

import sys

import numpy as np
from PyQt5 import QtWidgets, uic, QtCore


class MainWindow(QtWidgets.QMainWindow):

    def __init__(self, *args, **kwargs):
        super(MainWindow, self).__init__(*args, **kwargs)

        # Load the UI Page
        uic.loadUi('mainwindow.ui', self)

        self.position.setXRange(0, 60)
        self.position.setYRange(0, 120)
        self.position.setTitle("Position")
        self._add_padding_to_plot_widget(self.position)

    def showEvent(self, e) -> None:
        super().showEvent(e)
        self._add_padding_to_plot_widget(self.position)

    def plot(self):
        x = np.random.rand(100)
        y = np.random.rand(100)
        self.position.plot(x, y)

    @staticmethod
    def _add_padding_to_plot_widget(plot_widget, padding=0.1):
        """
        zooms out the view of a plot widget to show 'padding' around the contents of a PlotWidget
        :param plot_widget: The widget to add padding to
        :param padding: the percentage of padding expressed between 0.0 and 1.0
        :return:
        """

        width = plot_widget.sceneRect().width() * (1. + padding)
        height = plot_widget.sceneRect().height() * (1. + padding)
        center = plot_widget.sceneRect().center()
        zoom_rect = QtCore.QRectF(center.x() - width / 2., center.y() - height / 2., width, height)

        plot_widget.fitInView(zoom_rect)


def main():
    app = QtWidgets.QApplication(sys.argv)
    main = MainWindow()
    main.show()
    sys.exit(app.exec_())


if __name__ == '__main__':
    main()

output:

example of the output

Upvotes: 2

Ted
Ted

Reputation: 523

Currently, the graphs are plotting but the axis are overlapping the edges and cutting off the axis/numbers.

I am not quite sure what you mean, but I cannot comment. If you set the lower panning limits of both axis to zero, the two axis will overlap at zero. setLimit function is inherited from class ViewBox Edit: If you mean by cutting the number at max range just add a padding parameter to the setRange function to slightly widen the view range. See the example below.

In addition, I have added a title to the plots but they aren't showing either. How do I fit the plot correctly?

use setTitle function inherited form class PlotItem

A simple example based on op's code. Example

from PyQt5 import QtWidgets
import pyqtgraph as pg
import sys
import numpy as np

class MainWindow(QtWidgets.QMainWindow):

    def __init__(self, *args, **kwargs):
        super(MainWindow, self).__init__(*args, **kwargs)

        # Set geometry of mainwindow
        self.setGeometry(0, 0, 791, 597)
        self.setWindowTitle("Main Window")
        self.setStyleSheet("QWidget { background-color: white; }")

        # Set the central widget and its layout
        self.horizontalLayoutWidget= QtWidgets.QWidget()
        self.horizontalLayout = QtWidgets.QHBoxLayout()
        self.horizontalLayoutWidget.setLayout(self.horizontalLayout)
        self.setCentralWidget(self.horizontalLayoutWidget)
        
        # Set the spacing and margins of the layout
        self.horizontalLayout.setSpacing(16)
        self.horizontalLayout.setContentsMargins(10,10,10,10)
        
        # Add PlotWidget to layout
        self.speed = pg.PlotWidget()
        self.horizontalLayout.addWidget(self.speed)
        self.position = pg.PlotWidget()
        self.horizontalLayout.addWidget(self.position)

        # Set the view limit of x and y axis so that the axis will overlap at (0,0) 
        self.position.setLimits(xMin = 0, yMin = 0)
        # Set a small padding to avoid the number form cutting off the edge
        self.position.setXRange(0,60, padding= 0.02)
        self.position.setYRange(0,120)
        
        # Use setTitle function, which is inherited form class PlotItem 
        self.position.setTitle("Position")
        self.speed.setTitle("Speed")
        
        self.plot()

    def plot(self):
        data = np.random.rand(60)*100
        self.position.plot(data)

def main():
    app = QtWidgets.QApplication(sys.argv)
    main = MainWindow()
    main.show()
    sys.exit(app.exec_())

if __name__ == '__main__':         
    main()

Upvotes: 3

Related Questions