Table of Contents#
- Understanding PyQtGraph Bar Graph Components
- Prerequisites and Setup
- Creating a Basic Bar Graph
- Method 1: Accessing Bar Positions from Plot Data
- Method 2: Using Mouse Events for Interactive Positioning
- Best Practices & Performance Tips
- Complete Working Example
- Conclusion
- 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/2for horizontal positioning
2. Prerequisites and Setup#
Install required packages:
pip install pyqtgraph pyqt5 numpyImport libraries:
import pyqtgraph as pg
from pyqtgraph.Qt import QtWidgets
import numpy as np
import sys3. 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:#
optsdictionary 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
BarGraphItemfor seamless integration
6. Best Practices & Performance Tips#
-
Use Vectorized Operations: For static analysis
centers = bar_graph.opts['x'] + bar_graph.opts['width'] * 0.5 -
Cache Transformations:
vb = plot_widget.vb scene_coords = vb.mapSceneToView(event.scenePos()) # Expensive operation! -
Avoid Per-Frame Calculations: Only compute positions during events
-
Customization Options:
- Set label font:
self.hover_label.setFont(QtGui.QFont('Arial', 12)) - Add background:
self.hover_label.setFill(pg.mkBrush('w'))
- Set label font:
-
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
optsdictionary 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#
- Official PyQtGraph Documentation
- BarGraphItem Class Reference:
pg.BarGraphItemdocumentation - Coordinate Transformation:
ViewBox.mapSceneToView()andmapViewToScene() - Interactive Examples:
PyQtGraph examples repository on GitHub - Qt Event Handling:
Qt Documentation forQGraphicsSceneHoverEvent