text: import copy
  import signal
  from unittest import mock
  
  import matplotlib
  from matplotlib import pyplot as plt
  from matplotlib._pylab_helpers import Gcf
  
  import pytest
  
  
  @pytest.fixture
  def qt_core(request):
      backend, = request.node.get_closest_marker('backend').args
      qt_compat = pytest.importorskip('matplotlib.backends.qt_compat')
      QtCore = qt_compat.QtCore
  
      if backend == 'Qt4Agg':
          try:
              py_qt_ver = int(QtCore.PYQT_VERSION_STR.split('.')[0])
          except AttributeError:
              py_qt_ver = QtCore.__version_info__[0]
          if py_qt_ver != 4:
              pytest.skip('Qt4 is not available')
  
      return QtCore
  
  
  @pytest.mark.parametrize('backend', [
      # Note: the value is irrelevant; the important part is the marker.
      pytest.param('Qt4Agg', marks=pytest.mark.backend('Qt4Agg')),
      pytest.param('Qt5Agg', marks=pytest.mark.backend('Qt5Agg')),
  ])
  def test_fig_close(backend):
      # save the state of Gcf.figs
      init_figs = copy.copy(Gcf.figs)
  
      # make a figure using pyplot interface
      fig = plt.figure()
  
      # simulate user clicking the close button by reaching in
      # and calling close on the underlying Qt object
      fig.canvas.manager.window.close()
  
      # assert that we have removed the reference to the FigureManager
      # that got added by plt.figure()
      assert init_figs == Gcf.figs
  
  
  @pytest.mark.backend('Qt5Agg')
  def test_fig_signals(qt_core):
      # Create a figure
      plt.figure()
  
      # Access signals
      event_loop_signal = None
  
      # Callback to fire during event loop: save SIGINT handler, then exit
      def fire_signal_and_quit():
          # Save event loop signal
          nonlocal event_loop_signal
          event_loop_signal = signal.getsignal(signal.SIGINT)
  
          # Request event loop exit
          qt_core.QCoreApplication.exit()
  
      # Timer to exit event loop
      qt_core.QTimer.singleShot(0, fire_signal_and_quit)
  
      # Save original SIGINT handler
      original_signal = signal.getsignal(signal.SIGINT)
  
      # Use our own SIGINT handler to be 100% sure this is working
      def CustomHandler(signum, frame):
          pass
  
      signal.signal(signal.SIGINT, CustomHandler)
  
      # mainloop() sets SIGINT, starts Qt event loop (which triggers timer and
      # exits) and then mainloop() resets SIGINT
      matplotlib.backends.backend_qt5._BackendQT5.mainloop()
  
      # Assert: signal handler during loop execution is signal.SIG_DFL
      assert event_loop_signal == signal.SIG_DFL
  
      # Assert: current signal handler is the same as the one we set before
      assert CustomHandler == signal.getsignal(signal.SIGINT)
  
      # Reset SIGINT handler to what it was before the test
      signal.signal(signal.SIGINT, original_signal)
  
  
  @pytest.mark.parametrize(
      'qt_key, qt_mods, answer',
      [
          ('Key_A', ['ShiftModifier'], 'A'),
          ('Key_A', [], 'a'),
          ('Key_A', ['ControlModifier'], 'ctrl+a'),
          ('Key_Aacute', ['ShiftModifier'],
           '\N{LATIN CAPITAL LETTER A WITH ACUTE}'),
          ('Key_Aacute', [],
           '\N{LATIN SMALL LETTER A WITH ACUTE}'),
          ('Key_Control', ['AltModifier'], 'alt+control'),
          ('Key_Alt', ['ControlModifier'], 'ctrl+alt'),
          ('Key_Aacute', ['ControlModifier', 'AltModifier', 'MetaModifier'],
           'ctrl+alt+super+\N{LATIN SMALL LETTER A WITH ACUTE}'),
          ('Key_Backspace', [], 'backspace'),
          ('Key_Backspace', ['ControlModifier'], 'ctrl+backspace'),
          ('Key_Play', [], None),
      ],
      ids=[
          'shift',
          'lower',
          'control',
          'unicode_upper',
          'unicode_lower',
          'alt_control',
          'control_alt',
          'modifier_order',
          'backspace',
          'backspace_mod',
          'non_unicode_key',
      ]
  )
  @pytest.mark.parametrize('backend', [
      # Note: the value is irrelevant; the important part is the marker.
      pytest.param('Qt4Agg', marks=pytest.mark.backend('Qt4Agg')),
      pytest.param('Qt5Agg', marks=pytest.mark.backend('Qt5Agg')),
  ])
  def test_correct_key(backend, qt_core, qt_key, qt_mods, answer):
      """
      Make a figure.
      Send a key_press_event event (using non-public, qtX backend specific api).
      Catch the event.
      Assert sent and caught keys are the same.
      """
      qt_mod = qt_core.Qt.NoModifier
      for mod in qt_mods:
          qt_mod |= getattr(qt_core.Qt, mod)
  
      class _Event:
          def isAutoRepeat(self): return False
          def key(self): return getattr(qt_core.Qt, qt_key)
          def modifiers(self): return qt_mod
  
      def on_key_press(event):
          assert event.key == answer
  
      qt_canvas = plt.figure().canvas
      qt_canvas.mpl_connect('key_press_event', on_key_press)
      qt_canvas.keyPressEvent(_Event())
  
  
  @pytest.mark.backend('Qt5Agg')
  def test_dpi_ratio_change():
      """
      Make sure that if _dpi_ratio changes, the figure dpi changes but the
      widget remains the same physical size.
      """
  
      prop = 'matplotlib.backends.backend_qt5.FigureCanvasQT._dpi_ratio'
  
      with mock.patch(prop, new_callable=mock.PropertyMock) as p:
  
          p.return_value = 3
  
          fig = plt.figure(figsize=(5, 2), dpi=120)
          qt_canvas = fig.canvas
          qt_canvas.show()
  
          from matplotlib.backends.backend_qt5 import qApp
  
          # Make sure the mocking worked
          assert qt_canvas._dpi_ratio == 3
  
          size = qt_canvas.size()
  
          qt_canvas.manager.show()
          qt_canvas.draw()
          qApp.processEvents()
  
          # The DPI and the renderer width/height change
          assert fig.dpi == 360
          assert qt_canvas.renderer.width == 1800
          assert qt_canvas.renderer.height == 720
  
          # The actual widget size and figure physical size don't change
          assert size.width() == 600
          assert size.height() == 240
          assert qt_canvas.get_width_height() == (600, 240)
          assert (fig.get_size_inches() == (5, 2)).all()
  
          p.return_value = 2
  
          assert qt_canvas._dpi_ratio == 2
  
          qt_canvas.draw()
          qApp.processEvents()
          # this second processEvents is required to fully run the draw.
          # On `update` we notice the DPI has changed and trigger a
          # resize event to refresh, the second processEvents is
          # required to process that and fully update the window sizes.
          qApp.processEvents()
  
          # The DPI and the renderer width/height change
          assert fig.dpi == 240
          assert qt_canvas.renderer.width == 1200
          assert qt_canvas.renderer.height == 480
  
          # The actual widget size and figure physical size don't change
          assert size.width() == 600
          assert size.height() == 240
          assert qt_canvas.get_width_height() == (600, 240)
          assert (fig.get_size_inches() == (5, 2)).all()
  
          p.return_value = 1.5
  
          assert qt_canvas._dpi_ratio == 1.5
  
          qt_canvas.draw()
          qApp.processEvents()
          # this second processEvents is required to fully run the draw.
          # On `update` we notice the DPI has changed and trigger a
          # resize event to refresh, the second processEvents is
          # required to process that and fully update the window sizes.
          qApp.processEvents()
  
          # The DPI and the renderer width/height change
          assert fig.dpi == 180
          assert qt_canvas.renderer.width == 900
          assert qt_canvas.renderer.height == 360
  
          # The actual widget size and figure physical size don't change
          assert size.width() == 600
          assert size.height() == 240
          assert qt_canvas.get_width_height() == (600, 240)
          assert (fig.get_size_inches() == (5, 2)).all()
  
  
  @pytest.mark.backend('Qt5Agg')
  def test_subplottool():
      fig, ax = plt.subplots()
      with mock.patch(
              "matplotlib.backends.backend_qt5.SubplotToolQt.exec_",
              lambda self: None):
          fig.canvas.manager.toolbar.configure_subplots()
  
  
  @pytest.mark.backend('Qt5Agg')
  def test_figureoptions():
      fig, ax = plt.subplots()
      ax.plot([1, 2])
      ax.imshow([[1]])
      ax.scatter(range(3), range(3), c=range(3))
      with mock.patch(
              "matplotlib.backends.qt_editor._formlayout.FormDialog.exec_",
              lambda self: None):
          fig.canvas.manager.toolbar.edit_parameters()
  
  
  @pytest.mark.backend('Qt5Agg')
  def test_double_resize():
      # Check that resizing a figure twice keeps the same window size
      fig, ax = plt.subplots()
      fig.canvas.draw()
      window = fig.canvas.manager.window
  
      w, h = 3, 2
      fig.set_size_inches(w, h)
      assert fig.canvas.width() == w * matplotlib.rcParams['figure.dpi']
      assert fig.canvas.height() == h * matplotlib.rcParams['figure.dpi']
  
      old_width = window.width()
      old_height = window.height()
  
      fig.set_size_inches(w, h)
      assert window.width() == old_width
      assert window.height() == old_height
  
  
  @pytest.mark.backend("Qt5Agg")
  def test_canvas_reinit():
      from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg
  
      called = False
  
      def crashing_callback(fig, stale):
          nonlocal called
          fig.canvas.draw_idle()
          called = True
  
      fig, ax = plt.subplots()
      fig.stale_callback = crashing_callback
      # this should not raise
      canvas = FigureCanvasQTAgg(fig)
      fig.stale = True
      assert called
  