Embedding NetGraph EditableGraph in PyQt5

80 Views Asked by At

I try to embed the EditableGraph function into a PyQt5 application. I developed the following program for testing, based on solutions found online.

from PyQt5 import QtWidgets, QtCore
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.figure import Figure
from netgraph import EditableGraph, InteractiveGraph
class MainWindow(QtWidgets.QMainWindow):
     def __init__(self):
        super(MainWindow, self).__init__()
        self.canvas = FigureCanvas(Figure())
        self.canvas.ax = self.canvas.figure.add_subplot(111)
        self.editgraph = EditableGraph([(0, 1), (1,2), (2,0)], 
                                       ax=self.canvas.ax)

        # Enable key_press_event events:
        self.canvas.setFocusPolicy(QtCore.Qt.ClickFocus)
        self.canvas.setFocus()

        widget = QtWidgets.QWidget()
        self.setCentralWidget(widget)
        layout = QtWidgets.QVBoxLayout(widget)
        layout.addWidget(self.canvas)

if __name__ == "__main__":
    app = QtWidgets.QApplication([])
    w = MainWindow()
    w.show()
    app.exec_()

However I have the following error:

self.fig.canvas.manager.key_press = key_press_handler AttributeError: 'NoneType' object has no attribute 'key_press'

More in detail, the error message is:

Traceback (most recent call last):
  File "C:... \testit.py", line 26, in <module>
    w = MainWindow()
        ^^^^^^^^^^^^
  File "C:... \testit.py", line 13, in __init__
    self.graph = EditableGraph([(0, 1), (1,2), (2,0)], ax=self.canvas.ax)
                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C: ... \netgraph\_interactive_graph_classes.py", line 1971, in __init__
    self.fig.canvas.manager.key_press = key_press_handler
    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
AttributeError: 'NoneType' object has no attribute 'key_press'

When I exchange EditableGraph by InteractiveGraph, it works well.

I use the following dev version of NetGraph.

pip install https://github.com/paulbrodersen/netgraph/archive/dev.zip

I don't know where the problem originated from, and I couldn't find a solution on the Web. Can you please help me? This embedding is crucial for my application. Thank you.

1

There are 1 best solutions below

0
Franck On

Finally I found the solution. The issue is to properly assign a manager to the canvas. First, matplotlib.figure.Figure does not produce a manager while pyplot.figure() does. In the following program the result is None for fig1 while it corresponds to an object reference for fig2.

from matplotlib.figure import Figure
import matplotlib.pyplot as plt
fig1 = Figure()
fig2 = plt.figure()
print("fig1>",fig1.figure.canvas.manager)
print("fig2>",fig2.figure.canvas.manager)

However, merely replacing Figure() with plt.figure() is insufficient because the manager is not copied/exported/transmitted to the canvas. Therefore, this must be achieved explicitly. The final code is as follows:

from PyQt5 import QtWidgets, QtCore
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
from netgraph import EditableGraph
import matplotlib.pyplot as plt

class MainWindow(QtWidgets.QMainWindow):
    def __init__(self, *args, **kwargs):
        super(MainWindow, self).__init__(*args, **kwargs)
        fig = plt.figure() # use pyplot.figure()  instead of Figure()
        
        manager = fig.canvas.manager # keep the manager 
        self.canvas = FigureCanvas(fig) 
        self.canvas.ax = self.canvas.figure.add_subplot(111)
        self.canvas.figure.canvas.manager = manager # <-- assign the manager to the canvas figure.
        self.graph = EditableGraph([(0, 1), (1, 2), (2, 0)], ax=self.canvas.ax)

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

        widget = QtWidgets.QWidget()
        self.setCentralWidget(widget)
        layout = QtWidgets.QVBoxLayout(widget)
        layout.addWidget(self.canvas)


if __name__ == "__main__":
    app = QtWidgets.QApplication([])
    w = MainWindow()
    w.show()
    app.exec_()

The creation of a matplotlib canvas in PyQT5 is typically defined by a class. Here is the definition of such a class including the manager:

class MplCanvas(FigureCanvasQTAgg)
def __init__(self, parent=None, width=5, height=5, dpi=100):
    fig = plt.figure(figsize=(width, height), dpi=dpi)
    manager = fig.canvas.manager
    self.axes = fig.add_subplot(111)
    super(MplCanvas, self).__init__(fig)
    self.figure.canvas.manager = manager
    self.figure.subplots_adjust(left=0, bottom=0, right=1, top=1)