codelessgenie blog

PyQtGraph - Getting X & Y Position of Bar Graph

PyQtGraph is a powerful graphics and GUI library for Python that excels in performance for real-time data visualization, especially in scientific applications. When working with bar graphs, you'll often need to retrieve precise coordinates of bars for tasks like tooltips, interactions, or data point analysis. This guide provides a comprehensive walkthrough on obtaining the X and Y positions of bars in PyQtGraph using both static and interactive approaches. We'll cover key concepts, step-by-step implementations, and best practices.

2026-07

Table of Contents#

  1. Understanding PyQtGraph Bar Graph Components
  2. Prerequisites and Setup
  3. Creating a Basic Bar Graph
  4. Method 1: Accessing Bar Positions from Plot Data
  5. Method 2: Using Mouse Events for Interactive Positioning
  6. Best Practices & Performance Tips
  7. Complete Working Example
  8. Conclusion
  9. References

1. Understanding PyQtGraph Bar Graph Components#

  • Bars: Bars are drawn as part of a single QGraphicsItem, not as individual QGraphicsRectItem objects
  • Bar Positions: Defined by (x, height, width) triplets
  • Coordinate Systems:
    • Data Coordinates: Position in graph axes (e.g., (2, 5))
    • View Coordinates: Pixel position in widget
    • Bar Center Calculation: x + width/2 for horizontal positioning

2. Prerequisites and Setup#

Install required packages:

pip install pyqtgraph pyqt5 numpy

Import libraries:

import pyqtgraph as pg
from pyqtgraph.Qt import QtWidgets
import numpy as np
import sys

3. Creating a Basic Bar Graph#

# Initialize application
app = QtWidgets.QApplication(sys.argv)
 
# Create main window
win = QtWidgets.QMainWindow()
win.setWindowTitle('Bar Position Demo')
win.resize(800, 600)
 
# Create PlotWidget
plot_widget = pg.PlotWidget()
win.setCentralWidget(plot_widget)
 
# Sample data
x = np.array([1, 2, 3, 4])
heights = np.array([30, 20, 35, 25])
width = 0.6
 
# Create bar graph
bar_graph = pg.BarGraphItem(x=x, height=heights, width=width)
plot_widget.addItem(bar_graph)
 
win.show()
app.exec_()

4. Method 1: Accessing Bar Positions from Plot Data#

Static Position Retrieval#

Get positions directly from initialization parameters:

def get_bar_positions(bar_item):
    """Returns list of (center_x, height) tuples for all bars"""
    positions = []
    for i in range(len(bar_item.opts['x'])):
        x_center = bar_item.opts['x'][i] + bar_item.opts['width']/2
        height = bar_item.opts['height'][i]
        positions.append((x_center, height))
    return positions
 
# Usage:
bar_positions = get_bar_positions(bar_graph)
print("Bar Centers:", bar_positions)

Key Notes:#

  • opts dictionary stores initialization parameters
  • Works immediately after graph creation
  • Doesn't account for view transformations (pan/zoom)

5. Method 2: Using Mouse Events for Interactive Positioning#

Hover Detection Implementation#

class HoverBarGraph(pg.BarGraphItem):
    def __init__(self, **kwargs):
        self.plot_widget = kwargs.pop('plot_widget', None)
        super().__init__(**{k: v for k, v in kwargs.items() if k != 'plot_widget'})
        self.setAcceptHoverEvents(True)
        self.hover_label = pg.TextItem(anchor=(0.5, 1))
        if self.plot_widget:
            self.plot_widget.addItem(self.hover_label)
        self.hover_label.hide()
    
    def hoverMoveEvent(self, event):
        # Convert mouse position to data coordinates
        mouse_point = self.plot_widget.vb.mapSceneToView(event.scenePos())
        x_mouse = mouse_point.x()
        
        # Find closest bar
        bars_x = np.array(self.opts['x'])
        widths = np.full(len(bars_x), self.opts['width'])
        distances = np.abs(bars_x + widths/2 - x_mouse)
        closest_idx = np.argmin(distances)
        
        # Get bar dimensions
        bar_x = bars_x[closest_idx]
        height = self.opts['height'][closest_idx]
        bar_center_x = bar_x + widths[closest_idx]/2
        
        # Position label
        self.hover_label.setText(f"X: {bar_center_x:.2f}\nY: {height:.2f}")
        self.hover_label.setPos(bar_center_x, height)
        self.hover_label.show()
        
    def hoverLeaveEvent(self, event):
        self.hover_label.hide()
 
# Replace original bar graph:
bar_graph = HoverBarGraph(x=x, height=heights, width=width, plot_widget=plot_widget)
plot_widget.addItem(bar_graph)

Key Features:#

  • Real-time coordinate display on hover
  • Correctly handles pan/zoom
  • Dynamic text positioning
  • Inherits from BarGraphItem for seamless integration

6. Best Practices & Performance Tips#

  1. Use Vectorized Operations: For static analysis

    centers = bar_graph.opts['x'] + bar_graph.opts['width'] * 0.5
  2. Cache Transformations:

    vb = plot_widget.vb
    scene_coords = vb.mapSceneToView(event.scenePos())  # Expensive operation!
  3. Avoid Per-Frame Calculations: Only compute positions during events

  4. Customization Options:

    • Set label font: self.hover_label.setFont(QtGui.QFont('Arial', 12))
    • Add background: self.hover_label.setFill(pg.mkBrush('w'))
  5. Error Handling: Check bounds before accessing arrays:

    if 0 <= closest_idx < len(self.opts['height']):
        ...

7. Complete Working Example#

import sys
import numpy as np
import pyqtgraph as pg
from pyqtgraph.Qt import QtWidgets, QtCore
 
class MainWindow(QtWidgets.QMainWindow):
    def __init__(self):
        super().__init__()
        self.setWindowTitle('Bar Position Inspector')
        self.resize(1000, 700)
        
        # Create plot
        self.plot = pg.PlotWidget()
        self.setCentralWidget(self.plot)
        self.plot.setTitle('Revenue by Quarter')
        self.plot.setLabel('left', 'Revenue ($M)')
        self.plot.setLabel('bottom', 'Quarter')
        
        # Sample data
        quarters = [1, 2, 3, 4]
        revenue = [45, 33, 52, 27]
        
        # Create interactive bar graph
        self.bars = InteractiveBarGraph(
            x=quarters, height=revenue, width=0.7,
            brush='#1f77b4', hoverBrush='#ff7f0e',
            pen=pg.mkPen('k', width=1),
            plot_widget=self.plot
        )
        self.plot.addItem(self.bars)
        
class InteractiveBarGraph(pg.BarGraphItem):
    def __init__(self, plot_widget, **kwargs):
        super().__init__(**kwargs)
        self.plot = plot_widget
        self.setAcceptHoverEvents(True)
        self.label = pg.TextItem(color='k', fill='#FFFF70CC')
        self.label.setFont(QtGui.QFont('Arial', 10, QtGui.QFont.Bold))
        self.plot.addItem(self.label)
        self.label.hide()
        
    def hoverMoveEvent(self, event):
        pos = self.plot.vb.mapSceneToView(event.scenePos())
        bars_x = np.array(self.opts['x'])
        widths = np.full_like(bars_x, self.opts['width'])
        centers = bars_x + widths / 2
        
        idx = np.argmin(np.abs(centers - pos.x()))
        if event.isAccepted() and 0 <= idx < len(bars_x):
            bar_center = centers[idx]
            height = self.opts['height'][idx]
            
            self.label.setText(f"Q{int(bars_x[idx])}: ${height}M")
            self.label.setPos(bar_center, height * 1.05)  # Position above bar
            self.label.show()
            
    def hoverLeaveEvent(self, event):
        self.label.hide()
        
if __name__ == '__main__':
    app = QtWidgets.QApplication(sys.argv)
    window = MainWindow()
    window.show()
    sys.exit(app.exec_())

8. Conclusion#

Retrieving bar positions in PyQtGraph involves either:

  • Static access through the opts dictionary for pre-defined positions
  • Interactive detection via mouse events for dynamic use cases

The hover-based approach provides the most practical solution for user-facing applications. Remember to:

  • Always convert coordinates between scene and data spaces
  • Optimize calculations for performance-critical applications
  • Customize visual feedback to match your UI theme

PyQtGraph's flexibility makes it excellent for building sophisticated data inspection tools with minimal code.


9. References#

  1. Official PyQtGraph Documentation
  2. BarGraphItem Class Reference:
    pg.BarGraphItem documentation
  3. Coordinate Transformation:
    ViewBox.mapSceneToView() and mapViewToScene()
  4. Interactive Examples:
    PyQtGraph examples repository on GitHub
  5. Qt Event Handling:
    Qt Documentation for QGraphicsSceneHoverEvent