2

I don't know if I'm doing something wrong or if this is a bug in Matplotlib. I have a main figure with various objects in it. When the user clicks on an object/artist a separate window should pop up to show a table populated with information about that object. The table can be quite large so the window needs to be scrollable. I also want both windows to handle pick events and keyboard press events, ideally using the same event handler functions, although that's not critical.

I adapted the scrollable window implementation from the PyQt5 version here. Here's some dummy code that illustrates the issue:

import matplotlib

matplotlib.use('Qt5Agg')
import matplotlib.pyplot as plt
from PyQt5 import QtWidgets
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg
from matplotlib.figure import Figure
from matplotlib.lines import Line2D

class ScrollableWindow(QtWidgets.QMainWindow):

    def __init__(self, fig, windowTitle="Matplotlib"):
        QtWidgets.QMainWindow.__init__(self)
        self.widget = QtWidgets.QWidget()
        self.setCentralWidget(self.widget)
        self.widget.setLayout(QtWidgets.QVBoxLayout())
        self.widget.layout().setContentsMargins(0, 0, 0, 0)
        self.widget.layout().setSpacing(0)
        self.fig = fig
        self.canvas = FigureCanvasQTAgg(self.fig)
        self.canvas.draw()
        self.scroll = QtWidgets.QScrollArea(self.widget)
        self.scroll.setWidget(self.canvas)
        self.widget.layout().addWidget(self.scroll)
        self.setWindowTitle(windowTitle)

class MainClass(object):

    def __init__(self):
        self.scrollWindow = None
        self.tableFig = None

        lines = plt.plot([1, 2, 3], 'go-')
        lines[0].set_picker(True)

        canvas = plt.gcf().canvas
        canvas.mpl_connect('pick_event', self.onPick)
        canvas.mpl_connect('key_press_event', self.onKeyPress)

    def onPick(self, event):
        print("onPick called! Artist: %s" % event.artist)

        if isinstance(event.artist, Line2D):
            self.tableFig = self.createTableFigure()
            self.scrollWindow = ScrollableWindow(self.tableFig, "Table")
            canvas = self.tableFig.canvas
            canvas.mpl_connect('pick_event', self.onPick)
            canvas.mpl_connect('key_press_event', self.onKeyPress)
            self.scrollWindow.show()

    def onKeyPress(self, event):
        print('onKeyPress called! Key: ' + event.key)

    def createTableFigure(self):
        columnLabels = ('Length', 'Width', 'Height', 'Sold?')
        rowLabels = ['Jeep', 'Ferrari', 'Porsche']
        data = [[2.2, 1.6, 1.2, True],
                [2.1, 1.5, 1.4, False],
                [2.0, 1.4, 1.5, False]]

        tableFig = Figure(figsize=(6, 1))
        ax = tableFig.gca()
        ax.set_axis_off()
        table = matplotlib.table.table(ax, cellText=data, colLabels=columnLabels, rowLabels=rowLabels, cellLoc='center', loc='center')
        table.set_picker(True)
        return tableFig

m = MainClass()
plt.show()

The main window shows a line. If I click on the line the scrollable window appears with the table inside. If I click on the table you can see the onPick method is called as expected.

However, the onKeyPress method only gets called when I make the main window the current one. When the table window is selected key press events are ignored. Why?? Is this a bug in Matplotlib or should key-press events be handled in a different way to pick events? How do I make the table window respond to key-press events? I've seen PyQt-specific answers like this one about handling keyboard events but it would seem odd to have to do that when pick events work exactly as you'd expect. Maybe it's a mouse vs keyboard event thing. I'm very new to PyQt.

Using Python 2.7 and Matplotlib 2.2.2.

snark
  • 2,462
  • 3
  • 32
  • 63
  • 1
    The solution is given in [this question](https://stackoverflow.com/questions/22043549/matplotlib-and-qt-mouse-press-event-key-is-always-none). Once someone upvotes the answer, it can be marked as duplicate. – ImportanceOfBeingErnest Jul 07 '18 at 10:19
  • Thanks! I'm out for a few days but when I get back I'll check it out. – snark Jul 07 '18 at 20:03

1 Answers1

2

@ImportanceOfBeingErnest was right. If I change one of the import lines to:

from PyQt5 import QtWidgets, QtCore

and add these 2 lines immediately after self.canvas = FigureCanvasQTAgg(self.fig) in the scrollable window's constructor...

self.canvas.setFocusPolicy(QtCore.Qt.ClickFocus)
self.canvas.setFocus()

...then the table window responds to key press events as well.

snark
  • 2,462
  • 3
  • 32
  • 63