diff --git a/Framework/PythonInterface/mantid/BundlePython.cmake b/Framework/PythonInterface/mantid/BundlePython.cmake
index 4f9752453ad877029cfdccc92fadb5ee0d916805..0f0ec08f764d1a82d4198e870a57ccee95bfc073 100644
--- a/Framework/PythonInterface/mantid/BundlePython.cmake
+++ b/Framework/PythonInterface/mantid/BundlePython.cmake
@@ -15,6 +15,11 @@ if( MSVC )
   install ( DIRECTORY ${PYTHON_DIR}/Scripts DESTINATION bin PATTERN ".svn" EXCLUDE PATTERN ".git" EXCLUDE PATTERN "*_d.py" EXCLUDE )
   install ( DIRECTORY ${PYTHON_DIR}/tcl DESTINATION bin PATTERN ".svn" EXCLUDE PATTERN ".git" EXCLUDE )
   install ( FILES ${PYTHON_DIR}/python${PYTHON_MAJOR_VERSION}${PYTHON_MINOR_VERSION}.dll
-            ${PYTHON_DIR}/python${PYTHON_MAJOR_VERSION}.dll ${PYTHON_EXECUTABLE} ${PYTHONW_EXECUTABLE}
+            ${PYTHON_EXECUTABLE} ${PYTHONW_EXECUTABLE}
             DESTINATION bin )
+  # Python >= 3 has an minimal API DLL too
+  if ( EXISTS ${PYTHON_DIR}/python${PYTHON_MAJOR_VERSION}.dll )
+    install ( FILES ${PYTHON_DIR}/python${PYTHON_MAJOR_VERSION}.dll
+              DESTINATION bin )
+  endif()
 endif()
diff --git a/Framework/PythonInterface/mantid/plots/__init__.py b/Framework/PythonInterface/mantid/plots/__init__.py
index 45c96ede2bc51e6ca0ad7c285369ec85bef3d4d0..7ce38b4fc87b82bc384a9b413339977fb768d34b 100644
--- a/Framework/PythonInterface/mantid/plots/__init__.py
+++ b/Framework/PythonInterface/mantid/plots/__init__.py
@@ -38,7 +38,7 @@ from mantid.plots import helperfunctions, plotfunctions, plotfunctions3D
 from mantid.plots.utility import autoscale_on_update
 from mantid.plots.helperfunctions import get_normalize_by_bin_width
 from mantid.plots.scales import PowerScale, SquareScale
-from mantid.plots.utility import artists_hidden, MantidAxType
+from mantid.plots.utility import artists_hidden, MantidAxType, legend_set_draggable
 from mantidqt.widgets.plotconfigdialog.legendtabwidget import LegendProperties
 
 
@@ -501,7 +501,7 @@ class MantidAxes(Axes):
 
     def make_legend(self):
         if self.legend_ is None:
-            self.legend().draggable()
+            legend_set_draggable(self.legend(), True)
         else:
             props = LegendProperties.from_legend(self.legend_)
             LegendProperties.create_legend(props, self)
@@ -623,7 +623,7 @@ class MantidAxes(Axes):
 
                     # also remove the curve from the legend
                     if (not self.is_empty(self)) and self.legend_ is not None:
-                        self.legend().draggable()
+                        legend_set_draggable(self.legend(), True)
 
                 if new_kwargs:
                     _autoscale_on = new_kwargs.pop("autoscale_on_update", self.get_autoscale_on())
@@ -748,7 +748,7 @@ class MantidAxes(Axes):
                     container_new = []
                     # also remove the curve from the legend
                     if (not self.is_empty(self)) and self.legend_ is not None:
-                        self.legend().draggable()
+                        legend_set_draggable(self.legend(), True)
 
                 return container_new
 
diff --git a/Framework/PythonInterface/mantid/plots/modest_image/modest_image.py b/Framework/PythonInterface/mantid/plots/modest_image/modest_image.py
index 0d3544ff1f65d9cf829915eca13c02992f23f85b..88631d6fbf890291b5a782fae08bda2923c0e227 100644
--- a/Framework/PythonInterface/mantid/plots/modest_image/modest_image.py
+++ b/Framework/PythonInterface/mantid/plots/modest_image/modest_image.py
@@ -211,8 +211,6 @@ def imshow(axes, X, cmap=None, norm=None, aspect=None,
 
     Unlike matplotlib version, must explicitly specify axes
     """
-    if not axes._hold:
-        axes.cla()
     if norm is not None:
         assert(isinstance(norm, mcolors.Normalize))
     if aspect is None:
diff --git a/Framework/PythonInterface/mantid/plots/utility.py b/Framework/PythonInterface/mantid/plots/utility.py
index 4e02c746b12961434f6a1d815bab46bf307c7321..2e69e98908026787bb9fc91965b4fe7410910aed 100644
--- a/Framework/PythonInterface/mantid/plots/utility.py
+++ b/Framework/PythonInterface/mantid/plots/utility.py
@@ -11,10 +11,17 @@ from contextlib import contextmanager
 
 from matplotlib import cm
 from matplotlib.container import ErrorbarContainer
+from matplotlib.legend import Legend
 
 from enum import Enum
 
 
+if hasattr(Legend, "set_draggable"):
+    SET_DRAGGABLE_METHOD = "set_draggable"
+else:
+    SET_DRAGGABLE_METHOD = "draggable"
+
+
 # Any changes here must be reflected in the definition in
 # the C++ MplCpp/Plot.h header. See the comment in that file
 # for the reason for duplication.
@@ -117,6 +124,14 @@ def get_autoscale_limits(ax, axis):
         return locator.view_limits(axis_min, axis_max)
 
 
+def legend_set_draggable(legend, state, use_blit=False, update='loc'):
+    """Utility function to support varying Legend api around draggable status across
+    the versions of matplotlib we support. Function arguments match those from matplotlib.
+    See matplotlib documentation for argument descriptions
+    """
+    getattr(legend, SET_DRAGGABLE_METHOD)(state, use_blit, update)
+
+
 def zoom_axis(ax, coord, x_or_y, factor):
     """
     Zoom in around the value 'coord' along the given axis.
diff --git a/Framework/PythonInterface/test/python/mantid/plots/CMakeLists.txt b/Framework/PythonInterface/test/python/mantid/plots/CMakeLists.txt
index dfc7e590e48fab793a5be6a9b9fd9423b9ebd128..e62034a5b8d1aa84d48c8f19bc094de220ebbef2 100644
--- a/Framework/PythonInterface/test/python/mantid/plots/CMakeLists.txt
+++ b/Framework/PythonInterface/test/python/mantid/plots/CMakeLists.txt
@@ -3,11 +3,9 @@ add_subdirectory(modest_image)
 # mantid.dataobjects tests
 
 set(TEST_PY_FILES
-  helperfunctionsTest.py
-  plotfunctionsTest.py
-  plotfunctions3DTest.py
-  plots__init__Test.py
-  ScalesTest.py)
+    helperfunctionsTest.py plotfunctionsTest.py plotfunctions3DTest.py
+    plots__init__Test.py ScalesTest.py UtilityTest.py
+)
 
 check_tests_valid(${CMAKE_CURRENT_SOURCE_DIR} ${TEST_PY_FILES})
 
diff --git a/Framework/PythonInterface/test/python/mantid/plots/UtilityTest.py b/Framework/PythonInterface/test/python/mantid/plots/UtilityTest.py
new file mode 100644
index 0000000000000000000000000000000000000000..94d4fd2a3550c3444f8c8e18712c67c786876f0a
--- /dev/null
+++ b/Framework/PythonInterface/test/python/mantid/plots/UtilityTest.py
@@ -0,0 +1,37 @@
+# Mantid Repository : https://github.com/mantidproject/mantid
+#
+# Copyright © 2019 ISIS Rutherford Appleton Laboratory UKRI,
+#     NScD Oak Ridge National Laboratory, European Spallation Source
+#     & Institut Laue - Langevin
+# SPDX - License - Identifier: GPL - 3.0 +
+#  This file is part of the mantid package
+# Mantid Repository : https://github.com/mantidproject/mantid
+#
+# Copyright © 2018 ISIS Rutherford Appleton Laboratory UKRI,
+#     NScD Oak Ridge National Laboratory, European Spallation Source
+#     & Institut Laue - Langevin
+# SPDX - License - Identifier: GPL - 3.0 +
+from __future__ import absolute_import, division
+
+import unittest
+
+from mantid.plots.utility import legend_set_draggable
+from mantid.py3compat.mock import create_autospec
+from matplotlib.legend import Legend
+
+
+class UtilityTest(unittest.TestCase):
+
+    def test_legend_set_draggable(self):
+        legend = create_autospec(Legend)
+        args = (None, False, 'loc')
+        legend_set_draggable(legend, *args)
+
+        if hasattr(Legend, 'set_draggable'):
+            legend.set_draggable.assert_called_with(*args)
+        else:
+            legend.draggable.assert_called_with(*args)
+
+
+if __name__ == '__main__':
+    unittest.main()
diff --git a/qt/applications/workbench/workbench/plotting/plotscriptgenerator/__init__.py b/qt/applications/workbench/workbench/plotting/plotscriptgenerator/__init__.py
index 2325f12e15c02734ab029346b00bb5013a124301..9744431ef40ba85d2eb51f68d5bd8c24fa990eb4 100644
--- a/qt/applications/workbench/workbench/plotting/plotscriptgenerator/__init__.py
+++ b/qt/applications/workbench/workbench/plotting/plotscriptgenerator/__init__.py
@@ -11,6 +11,7 @@ from __future__ import (absolute_import, unicode_literals)
 from mantid.plots import MantidAxes
 
 from mantidqt.widgets.plotconfigdialog import curve_in_ax
+from matplotlib.legend import Legend
 from workbench.plugins.editor import DEFAULT_CONTENT
 from workbench.plotting.plotscriptgenerator.axes import (generate_axis_limit_commands,
                                                          generate_axis_label_commands,
@@ -22,6 +23,10 @@ from workbench.plotting.plotscriptgenerator.utils import generate_workspace_retr
 
 FIG_VARIABLE = "fig"
 AXES_VARIABLE = "axes"
+if hasattr(Legend, "set_draggable"):
+    SET_DRAGGABLE_METHOD = "set_draggable"
+else:
+    SET_DRAGGABLE_METHOD = "draggable"
 
 
 def generate_script(fig, exclude_headers=False):
@@ -107,7 +112,8 @@ def get_title_cmds(ax, ax_object_var):
 def get_legend_cmds(ax, ax_object_var):
     """Get command axes.set_legend"""
     if ax.legend_:
-        return ["{ax_obj}.legend().draggable()".format(ax_obj=ax_object_var)]
+        return ["{ax_obj}.legend().{draggable_method}()".format(ax_obj=ax_object_var,
+                                                                draggable_method=SET_DRAGGABLE_METHOD)]
     return []
 
 
diff --git a/qt/applications/workbench/workbench/plotting/plotscriptgenerator/test/test_plotscriptgenerator.py b/qt/applications/workbench/workbench/plotting/plotscriptgenerator/test/test_plotscriptgenerator.py
index 7cd871bac2d703572a5e8ae5db936421c05b7fcf..1c44cf2ad473514918066710ebc3083f49d72c8a 100644
--- a/qt/applications/workbench/workbench/plotting/plotscriptgenerator/test/test_plotscriptgenerator.py
+++ b/qt/applications/workbench/workbench/plotting/plotscriptgenerator/test/test_plotscriptgenerator.py
@@ -11,6 +11,7 @@ import unittest
 import matplotlib
 matplotlib.use("Agg")  # noqa
 from matplotlib.axes import Axes
+from matplotlib.legend import Legend
 
 from mantid.plots import MantidAxes
 from mantid.py3compat.mock import Mock, patch
@@ -120,7 +121,10 @@ class PlotScriptGeneratorTest(unittest.TestCase):
 
         mock_ax = self._gen_mock_axes(legend_=True)
         mock_fig = Mock(get_axes=lambda: [mock_ax])
-        self.assertIn('.legend().draggable()', generate_script(mock_fig))
+        if hasattr(Legend, "set_draggable"):
+            self.assertIn('.legend().set_draggable()', generate_script(mock_fig))
+        else:
+            self.assertIn('.legend().draggable()', generate_script(mock_fig))
 
     @patch(GET_AUTOSCALE_LIMITS)
     @patch(GEN_WS_RETRIEVAL_CMDS)
diff --git a/qt/applications/workbench/workbench/plotting/toolbar.py b/qt/applications/workbench/workbench/plotting/toolbar.py
index 53018e5862db88511b1d4bd4b3fbfdcd2f3aaa53..d0b7a5bdd38f2068cd4944f6271beb1f84c22684 100644
--- a/qt/applications/workbench/workbench/plotting/toolbar.py
+++ b/qt/applications/workbench/workbench/plotting/toolbar.py
@@ -76,7 +76,6 @@ class WorkbenchNavigationToolbar(NavigationToolbar2QT):
                 if tooltip_text is not None:
                     a.setToolTip(tooltip_text)
 
-        self.buttons = {}
         # Add the x,y location widget at the right side of the toolbar
         # The stretch factor is 1 which means any resizing of the toolbar
         # will resize this label instead of the buttons.
@@ -88,9 +87,6 @@ class WorkbenchNavigationToolbar(NavigationToolbar2QT):
             labelAction = self.addWidget(self.locLabel)
             labelAction.setVisible(True)
 
-        # reference holder for subplots_adjust window
-        self.adj_window = None
-
         # Adjust icon size or they are too small in PyQt5 by default
         dpi_ratio = QtWidgets.QApplication.instance().desktop().physicalDpiX() / 100
         self.setIconSize(QtCore.QSize(24 * dpi_ratio, 24 * dpi_ratio))
diff --git a/qt/python/mantidqt/widgets/codeeditor/completion.py b/qt/python/mantidqt/widgets/codeeditor/completion.py
index 3da3608a69e8f901aea1d1ba1e2b86e1365a6303..d5290d19f1ff06ca625b9dd5248965869e04d27c 100644
--- a/qt/python/mantidqt/widgets/codeeditor/completion.py
+++ b/qt/python/mantidqt/widgets/codeeditor/completion.py
@@ -28,11 +28,13 @@ revisiting when we move to Python 3.
 from __future__ import (absolute_import, unicode_literals)
 
 import ast
+from collections import namedtuple
+import contextlib
 import inspect
+from keyword import kwlist as python_keywords
 import re
 import sys
-from keyword import kwlist as python_keywords
-from collections import namedtuple
+import warnings
 
 from lib2to3.pgen2.tokenize import detect_encoding
 from io import BytesIO
@@ -48,6 +50,16 @@ from mantidqt.widgets.codeeditor.editor import CodeEditor
 ArgSpec = namedtuple("ArgSpec", "args varargs keywords defaults")
 
 
+@contextlib.contextmanager
+def _ignore_matplotlib_deprecation_warnings():
+    """Context-manager to disable deprecation warnings from matplotlib while
+    generating the call tips"""
+    from matplotlib.cbook import MatplotlibDeprecationWarning
+    with warnings.catch_warnings():
+        warnings.simplefilter("ignore", MatplotlibDeprecationWarning)
+        yield
+
+
 def get_builtin_argspec(builtin):
     """
     Get the call tips for a builtin function from its docstring
@@ -233,7 +245,8 @@ class CodeCompleter(object):
         if re.search("^#{0}import .*numpy( |,|$)", self.editor.text(), re.MULTILINE):
             self._add_to_completions(self._get_module_call_tips('numpy'))
         if re.search("^#{0}import .*pyplot( |,|$)", self.editor.text(), re.MULTILINE):
-            self._add_to_completions(self._get_module_call_tips('matplotlib.pyplot'))
+            with _ignore_matplotlib_deprecation_warnings():
+                self._add_to_completions(self._get_module_call_tips('matplotlib.pyplot'))
         self._add_to_completions(python_keywords)
 
         self.editor.enableAutoCompletion(CodeEditor.AcsAPIs)
@@ -251,7 +264,8 @@ class CodeCompleter(object):
             self._completions_dict[completion] = True
 
     def update_completion_api(self):
-        self._add_to_completions(self._get_completions_from_globals())
+        with _ignore_matplotlib_deprecation_warnings():
+            self._add_to_completions(self._get_completions_from_globals())
         self.editor.updateCompletionAPI(self.completions)
 
     def _get_module_call_tips(self, module):
diff --git a/qt/python/mantidqt/widgets/plotconfigdialog/legendtabwidget/__init__.py b/qt/python/mantidqt/widgets/plotconfigdialog/legendtabwidget/__init__.py
index 72c00ca55b822bd7a2e3169f8738e04254909735..5e5ffb2aea1f1e66d05ccd6c287e3fe352bb7dba 100644
--- a/qt/python/mantidqt/widgets/plotconfigdialog/legendtabwidget/__init__.py
+++ b/qt/python/mantidqt/widgets/plotconfigdialog/legendtabwidget/__init__.py
@@ -8,6 +8,7 @@
 
 from __future__ import (absolute_import, unicode_literals)
 
+from mantid.plots.utility import legend_set_draggable
 from mantidqt.widgets.plotconfigdialog.colorselector import convert_color_to_hex
 import matplotlib
 from matplotlib.patches import BoxStyle
@@ -157,4 +158,4 @@ class LegendProperties(dict):
 
         legend.set_visible(props['visible'])
 
-        legend.draggable(True)
+        legend_set_draggable(legend, True)
diff --git a/qt/python/mantidqt/widgets/workspacedisplay/table/presenter.py b/qt/python/mantidqt/widgets/workspacedisplay/table/presenter.py
index 00c7401a01149f9a755af4ce33dd8f0eec9951a5..1390419ee2610ca719a289936277695c5e9a7f80 100644
--- a/qt/python/mantidqt/widgets/workspacedisplay/table/presenter.py
+++ b/qt/python/mantidqt/widgets/workspacedisplay/table/presenter.py
@@ -12,6 +12,7 @@ from functools import partial
 from qtpy.QtCore import Qt
 
 from mantid.kernel import logger
+from mantid.plots.utility import legend_set_draggable
 from mantidqt.widgets.observers.ads_observer import WorkspaceDisplayADSObserver
 from mantidqt.widgets.observers.observing_presenter import ObservingPresenter
 from mantidqt.widgets.workspacedisplay.data_copier import DataCopier
@@ -358,7 +359,7 @@ class TableWorkspaceDisplay(ObservingPresenter, DataCopier):
                 return
 
             ax.set_ylabel(column_label)
-        ax.legend().draggable()
+        legend_set_draggable(ax.legend(), True)
         fig.show()
 
     def _get_plot_function_from_type(self, ax, type):
diff --git a/qt/widgets/common/src/Python/Sip.cpp b/qt/widgets/common/src/Python/Sip.cpp
index c7a2e42958614cd69b26290653131175d6a2bb9a..3102358e82e2e0424c52a57b66e6834b7bf8c78a 100644
--- a/qt/widgets/common/src/Python/Sip.cpp
+++ b/qt/widgets/common/src/Python/Sip.cpp
@@ -6,6 +6,7 @@
 // SPDX - License - Identifier: GPL - 3.0 +
 
 #include "MantidQtWidgets/Common/Python/Sip.h"
+#include <QtGlobal>
 #include <sip.h>
 
 namespace MantidQt {
@@ -21,30 +22,23 @@ const sipAPIDef *sipAPI() {
   static const sipAPIDef *sip_API = nullptr;
   if (sip_API)
     return sip_API;
-#if defined(SIP_USE_PYCAPSULE)
-  sip_API = (const sipAPIDef *)PyCapsule_Import("sip._C_API", 0);
-#else
-  /* Import the SIP module. */
-  PyObject *sip_module = PyImport_ImportModule("sip");
-  if (sip_module == NULL)
-    throw std::runtime_error("sip_api() - Error importing sip module");
-
-  /* Get the module's dictionary. */
-  PyObject *sip_module_dict = PyModule_GetDict(sip_module);
-
-  /* Get the "_C_API" attribute. */
-  PyObject *c_api = PyDict_GetItemString(sip_module_dict, "_C_API");
-  if (c_api == NULL)
-    throw std::runtime_error(
-        "sip_api() - Unable to find _C_API attribute in sip dictionary");
 
-  /* Sanity check that it is the right type. */
-  if (!PyCObject_Check(c_api))
-    throw std::runtime_error("sip_api() - _C_API type is not a CObject");
-
-  /* Get the actual pointer from the object. */
-  sip_API = (const sipAPIDef *)PyCObject_AsVoidPtr(c_api);
+    // Some configs have a private sip module inside PyQt. Try this first
+#if QT_VERSION < QT_VERSION_CHECK(5, 0, 0)
+  sip_API = (const sipAPIDef *)PyCapsule_Import("PyQt4.sip._C_API", 0);
+#elif QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) &&                               \
+    QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
+  sip_API = (const sipAPIDef *)PyCapsule_Import("PyQt5.sip._C_API", 0);
+#else
+#error "Unknown sip module for Qt >= 6"
 #endif
+  // Try plain sip module
+  if (!sip_API) {
+    PyErr_Clear();
+    sip_API = (const sipAPIDef *)PyCapsule_Import("sip._C_API", 0);
+  }
+
+  assert(sip_API);
   return sip_API;
 }
 } // namespace Detail
@@ -52,4 +46,4 @@ const sipAPIDef *sipAPI() {
 } // namespace Python
 } // namespace Common
 } // namespace Widgets
-} // namespace MantidQt
\ No newline at end of file
+} // namespace MantidQt
diff --git a/scripts/PyChop/PyChopGui.py b/scripts/PyChop/PyChopGui.py
index 5cfe88870ddd7fc7a87a134e9beeb9adc377ff6f..1e052cfadf6c110124342438008993a44788acdd 100755
--- a/scripts/PyChop/PyChopGui.py
+++ b/scripts/PyChop/PyChopGui.py
@@ -27,6 +27,7 @@ from qtpy.QtCore import (QEventLoop, Qt)  # noqa
 from qtpy.QtWidgets import (QAction, QCheckBox, QComboBox, QDialog, QFileDialog, QGridLayout, QHBoxLayout, QMenu, QLabel,
                             QLineEdit, QMainWindow, QMessageBox, QPushButton, QSizePolicy, QSpacerItem, QTabWidget,
                             QTextEdit, QVBoxLayout, QWidget)  # noqa
+from mantid.plots.utility import legend_set_draggable
 from mantidqt.MPLwidgets import FigureCanvasQTAgg as FigureCanvas
 from mantidqt.MPLwidgets import NavigationToolbar2QT as NavigationToolbar
 import matplotlib
@@ -280,7 +281,7 @@ class PyChopGui(QMainWindow):
                 self.plot_qe(ei, label_text, overplot)
             self.resaxes_xlim = max(ei, self.resaxes_xlim)
         self.resaxes.set_xlim([0, self.resaxes_xlim])
-        self.resaxes.legend().draggable()
+        legend_set_draggable(self.resaxes.legend(), True)
         self.resaxes.set_xlabel('Energy Transfer (meV)')
         self.resaxes.set_ylabel(r'$\Delta$E (meV FWHM)')
         self.rescanvas.draw()
@@ -299,7 +300,7 @@ class PyChopGui(QMainWindow):
         line, = self.qeaxes.plot(np.hstack(q2), np.concatenate((np.flipud(en), en)).tolist() * len(self.engine.detector.tthlims))
         line.set_label(label_text)
         self.qeaxes.set_xlim([0, self.qeaxes_xlim])
-        self.qeaxes.legend().draggable()
+        legend_set_draggable(self.qesaxes.legend(), True)
         self.qeaxes.set_xlabel(r'$|Q| (\mathrm{\AA}^{-1})$')
         self.qeaxes.set_ylabel('Energy Transfer (meV)')
         self.qecanvas.draw()
@@ -371,7 +372,7 @@ class PyChopGui(QMainWindow):
         self.flxaxes1.set_xlabel('Incident Energy (meV)')
         self.flxaxes2.set_ylabel('Elastic Resolution FWHM (meV)')
         lg = self.flxaxes2.legend()
-        lg.draggable()
+        legend_set_draggable(lg, True)
         self.flxcanvas.draw()
 
     def update_slider(self, val=None):
@@ -438,7 +439,7 @@ class PyChopGui(QMainWindow):
         line, = self.frqaxes2.plot(freqs, elres, 'o-')
         line.set_label('%s "%s" Ei = %5.3f meV' % (inst, chop, ei))
         lg = self.frqaxes2.legend()
-        lg.draggable()
+        legend_set_draggable(lg, True)
         self.frqaxes2.set_xlim([0, np.max(freqs)])
         self.frqcanvas.draw()