'Click scatter plot to get the cordinate's information
I'm trying to make a GUI that displays simple scatter plot making use of PyQt5 like below. I want implement a QLabel object at the bottom of GUI that displays the coordinate's information when I clicked certain data point on the plot. Is there anyone who has a good solution?
import sys
import pandas as pd
from PyQt5 import QtCore, QtWidgets
from PyQt5.QtCore import Qt
from PyQt5.QtWidgets import QVBoxLayout, QWidget, QLabel
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as canvas
from matplotlib.backends.backend_qt5agg import NavigationToolbar2QT as NavigationToolbar
from matplotlib.figure import Figure
class MplCanvas(canvas):
def __init__(self, parent=None, width=5, height=4, dpi=100):
fig = Figure(figsize=(width, height), dpi=dpi)
self.axes = fig.add_subplot(111)
super().__init__(fig)
class TableModel(QtCore.QAbstractTableModel):
def __init__(self, data):
super().__init__()
self._data = data
def data(self, index, role=Qt.DisplayRole):
if index.isValid():
if role == Qt.DisplayRole or role == Qt.EditRole:
value = self._data.iloc[index.row(), index.column()]
return str(value)
def setData(self, index, value, role):
if role == Qt.EditRole:
self._data.iloc[index.row(), index.column()] = float(value)
return True
return False
def headerData(self, col, orientation, role):
if orientation == Qt.Horizontal and role == Qt.DisplayRole:
return self._data.columns[col]
def flags(self, index):
return Qt.ItemIsSelectable | Qt.ItemIsEnabled | Qt.ItemIsEditable
def rowCount(self, index):
return self._data.shape[0]
def columnCount(self, index):
return self._data.shape[1]
def headerData(self, section, orientation, role):
if role == Qt.DisplayRole:
if orientation == Qt.Horizontal:
return str(self._data.columns[section])
if orientation == Qt.Vertical:
return str(self._data.index[section])
class MainWindow(QtWidgets.QMainWindow):
def __init__(self):
super().__init__()
layout = QVBoxLayout()
self.table = QtWidgets.QTableView()
self.data = pd.DataFrame({'X': [10, 20, 30, 40, 50, 60, 70, 80, 90, 100],
'Y': [20, 21, 19, 18, 50, 43, 12, 77, 34, 56],
'Z': [65, 34, 34, 90, 45, 23, 34, 54, 23, 12],
'A': [33, 56, 34, 12, 76, 45, 22, 87, 45, 20],
'B': ['aa', 'aa', 'bb','bb', 'bb', 'bb', 'cc', 'cc', 'cc', 'cc']})
# Pandas data model setting
self.model = TableModel(self.data)
self.table.setModel(self.model)
# define scatterplot
sc = MplCanvas(self, width=5, height=4, dpi=100)
self.plot = self.data.plot.scatter(x='X', y='Y', c='Z', colormap='jet', ax=sc.axes)
toolbar = NavigationToolbar(sc, self)
# Refresh button
info_display = QLabel("X= , Y= , Z= ")
layout.addWidget(self.table)
layout.addWidget(toolbar)
layout.addWidget(sc)
layout.addWidget(info_display)
widget = QWidget()
widget.setLayout(layout)
self.setCentralWidget(widget)
def refresh_btn(self):
self.plot.clear()
self.plot.scatter(self.data["X"], self.data["Y"], c=self.data["Z"], cmap="jet", s=20, alpha=0.9)
self.plot.figure.canvas.draw()
self.plot.figure.canvas.flush_events()
app = QtWidgets.QApplication(sys.argv)
window = MainWindow()
window.show()
app.exec_()
enter image description here
Solution 1:[1]
This needs to be handled by the matplotlib picker events. This allows you to detect selected data points on the plot, returned as an array of indexes in the original data.
First enable the picker on the plot when creating it.
# define scatterplot
sc = MplCanvas(self, width=5, height=4, dpi=100)
self.plot = self.data.plot.scatter(x='X', y='Y', c='Z', colormap='jet', ax=sc.axes, picker=True, pickradius=5)
toolbar = NavigationToolbar(sc, self)
Then hook up the picker event to a custom method (here self.on_pick
).
# Connect pick event
self.plot.figure.canvas.mpl_connect('pick_event', self.on_pick)
Finally, you need to define the handler method.
def on_pick(self, event):
indexes = event.ind # Indexes of the data point (array).
i = indexes[0] # Only one selection for demo.
print("Data point:", self.data["X"][i], self.data["Y"][i], self.data["Z"][i])
# Highlight the row, through the model.
self.table.selectRow(i)
The event
object has an attribute .ind
which contains an array of indexes of the clicked points (above we just select the first one). We print out the data, and then use the same index to select the current row in the table.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | mfitzp |