diff --git a/CMakeLists.txt b/CMakeLists.txt
index 4f45d8c16ac91137b442bac6dc12c7c97762791b..1a104fc10c81b14bf2a13499d47309e2474f3bc6 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -127,8 +127,14 @@ if(ENABLE_MANTIDPLOT)
     message(FATAL_ERROR "Qwt version 5 is required, found: ${QWT5_VERSION}")
   endif()
 
-  find_package(PyQt4 REQUIRED)
-  find_package(SIP REQUIRED)
+  if(PYTHON_VERSION_MAJOR EQUAL 3 AND
+     UNIX_DIST MATCHES "RedHatEnterprise" AND UNIX_RELEASE MATCHES "7\..*")
+    set(USE_PRIVATE_SIPPYQT4 ON)
+    include(ExternalSipPyQt4)
+  else()
+    find_package(PyQt4 REQUIRED)
+    find_package(SIP REQUIRED)
+  endif()
   separate_arguments(PYQT4_SIP_FLAGS)
 endif()
 
@@ -382,58 +388,64 @@ if(ENABLE_CPACK)
     # rhel requirements - only used if package requested is rpm
     set(
       CPACK_RPM_PACKAGE_REQUIRES
-      "nexus >= 4.3.1,gsl,glibc,muParser,numpy,h5py >= 2.3.1,PyCifRW >= 4.2.1,tbb,librdkafka,"
-      "${CPACK_RPM_PACKAGE_REQUIRES},OCE-draw,OCE-foundation,OCE-modeling,OCE-ocaf,OCE-visualization,"
+      "boost169-date-time,"
+      "boost169-regex,"
+      "boost169-serialization,"
+      "boost169-filesystem,"
+      "boost169-system,"
+      "nexus >= 4.3.1,gsl,glibc,muParser,tbb,librdkafka,"
+      "OCE-draw,OCE-foundation,OCE-modeling,OCE-ocaf,OCE-visualization,"
       "poco-crypto,poco-data,poco-mysql,poco-sqlite,poco-odbc,poco-util,poco-xml,poco-zip,poco-net,poco-netssl,poco-foundation,"
-      "sip >= 4.18,python-enum34,"
-      "python-six,python-ipython >= 1.1.0,python-ipython-notebook,PyYAML,"
-      "python-requests,"
-      "scipy,"
       "hdf,hdf5,jsoncpp >= 0.7.0")
     if(ENABLE_MANTIDPLOT)
       set(CPACK_RPM_PACKAGE_REQUIRES
-          "${CPACK_RPM_PACKAGE_REQUIRES},qt4 >= 4.2,PyQt4,qwtplot3d-qt4")
+          "${CPACK_RPM_PACKAGE_REQUIRES},qt4 >= 4.2,qscintilla,qwt5-qt4,qwtplot3d-qt4")
     endif()
     if(ENABLE_WORKBENCH)
       set(CPACK_RPM_PACKAGE_REQUIRES
-          "${CPACK_RPM_PACKAGE_REQUIRES},qt5-qtbase,python2-qt5")
+          "${CPACK_RPM_PACKAGE_REQUIRES},qt5-qtbase,qscintilla-qt5")
     endif()
 
-    if("${UNIX_CODENAME}" MATCHES "Santiago") # RHEL6
-      if(ENABLE_WORKBENCH)
-        message(FATAL_ERROR "mantidworkbench is not supported on RHEL6")
-      endif()
-      # On RHEL6 we have to use an updated qscintilla to fix an auto complete
-      # bug
+    if(PYTHON_VERSION_MAJOR EQUAL 3)
       set(
         CPACK_RPM_PACKAGE_REQUIRES
-        "${CPACK_RPM_PACKAGE_REQUIRES},qscintilla >= 2.4.6,boost157,python-matplotlib,"
-        # On RHEL6 we are using SCL packages for Qt
-        "scl-utils,mantidlibs34,mantidlibs34-runtime,mantidlibs34-qt,mantidlibs34-qt-x11,mantidlibs34-qt-webkit,mantidlibs34-qwt5-qt4"
-        )
-    else() # assumes RHEL7 Require matplotlib >= 1.5 to fix bug in latex
-           # rendering - oddly matplotlib-qt4 contains qt5 backend as well
+        "${CPACK_RPM_PACKAGE_REQUIRES},"
+        "boost169-python3,"
+        "python36-numpy,python36-scipy,python36-h5py,python36-PyCifRW,"
+        "python36-six,python36-PyYAML,python36-requests,"
+        "python36-ipython,python36-ipython-notebook"
+      )
+      if(ENABLE_MANTIDPLOT)
+        set(CPACK_RPM_PACKAGE_REQUIRES
+            "${CPACK_RPM_PACKAGE_REQUIRES},python36-QtPy,"
+            "python36-matplotlib-qt4,python36-ipython-gui")
+      endif()
+      if(ENABLE_WORKBENCH)
+        set(CPACK_RPM_PACKAGE_REQUIRES
+            "${CPACK_RPM_PACKAGE_REQUIRES},python36-qt5,python36-QtPy,"
+            "python36-matplotlib-qt5,python36-psutil,python36-ipython-gui")
+      endif()
+    else()
       set(
         CPACK_RPM_PACKAGE_REQUIRES
         "${CPACK_RPM_PACKAGE_REQUIRES},"
-        "boost169-date-time,"
-        "boost169-regex,"
-        "boost169-serialization,"
-        "boost169-filesystem,"
-        "boost169-system,"
         "boost169-python2,"
-        "python2-QtPy,"
-        "python2-matplotlib-qt4 >= 1.5.2"
-        )
+        "numpy,scipy,h5py >= 2.3.1,PyCifRW >= 4.2.1,"
+        "python-enum34,python-six,PyYAML,python-requests,"
+        "python-ipython >= 1.1.0,python-ipython-notebook"
+      )
       if(ENABLE_MANTIDPLOT)
         set(CPACK_RPM_PACKAGE_REQUIRES
-            "${CPACK_RPM_PACKAGE_REQUIRES},qscintilla,qwt5-qt4")
+            "${CPACK_RPM_PACKAGE_REQUIRES},sip >= 4.18,PyQt4,python2-QtPy,"
+            "python2-matplotlib-qt4 >= 1.5.2,python-ipython-gui")
       endif()
       if(ENABLE_WORKBENCH)
         set(CPACK_RPM_PACKAGE_REQUIRES
-            "${CPACK_RPM_PACKAGE_REQUIRES},qscintilla-qt5,python-psutil")
+            "${CPACK_RPM_PACKAGE_REQUIRES},python2-qt5,python2-QtPy,"
+            "python2-matplotlib-qt4 >= 1.5.2,python-psutil,python-ipython-gui")
       endif()
     endif()
+
     string(REPLACE ";"
                    ","
                    CPACK_RPM_PACKAGE_REQUIRES
diff --git a/Framework/PostInstall/CMakeLists.txt b/Framework/PostInstall/CMakeLists.txt
index 5e996f052745df4fd8011fc0a2cef5adb6e50d97..7f94a7725d63df9f635117c13c3e0fa89f59c910 100644
--- a/Framework/PostInstall/CMakeLists.txt
+++ b/Framework/PostInstall/CMakeLists.txt
@@ -12,7 +12,7 @@
 # ${DESTDIR}${CMAKE_INSTALL_PREFIX} but this should be evaluated at CPack time
 # so escape the $ to its written literally.
 set(PACKAGE_ROOT \$ENV{DESTDIR}\${CMAKE_INSTALL_PREFIX})
-set(EXCLUDE_REGEX ".*_template|.*port_v3")
+set(EXCLUDE_REGEX ".*_template")
 
 set(COMPILE_SCRIPT "message ( \"Byte-compiling Python in ${PACKAGE_ROOT}\")")
 set(
diff --git a/Framework/PythonInterface/mantid/plots/__init__.py b/Framework/PythonInterface/mantid/plots/__init__.py
index 7ce38b4fc87b82bc384a9b413339977fb768d34b..8031d9567800cf57ebe109b57404da199b6ba535 100644
--- a/Framework/PythonInterface/mantid/plots/__init__.py
+++ b/Framework/PythonInterface/mantid/plots/__init__.py
@@ -19,7 +19,7 @@ try:
    from collections.abc import Iterable
 except ImportError:
    # check Python 2 location
-   from collections import Iterable   
+   from collections import Iterable
 from matplotlib.axes import Axes
 from matplotlib.collections import Collection
 from matplotlib.colors import Colormap
@@ -35,11 +35,11 @@ from mpl_toolkits.mplot3d.axes3d import Axes3D
 from mantid.api import AnalysisDataService as ads
 from mantid.kernel import logger
 from mantid.plots import helperfunctions, plotfunctions, plotfunctions3D
-from mantid.plots.utility import autoscale_on_update
+from mantid.plots.legend import LegendProperties
 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, legend_set_draggable
-from mantidqt.widgets.plotconfigdialog.legendtabwidget import LegendProperties
+from mantid.plots.utility import (artists_hidden, autoscale_on_update,
+                                  legend_set_draggable, MantidAxType)
 
 
 def plot_decorator(func):
diff --git a/Framework/PythonInterface/mantid/plots/legend.py b/Framework/PythonInterface/mantid/plots/legend.py
new file mode 100644
index 0000000000000000000000000000000000000000..5cf25f89661cb7ddd62b5054bbab13c4b6bb12d8
--- /dev/null
+++ b/Framework/PythonInterface/mantid/plots/legend.py
@@ -0,0 +1,174 @@
+# Mantid Repository : https://github.com/mantidproject/mantid
+#
+# Copyright © 2020 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
+#
+#
+"""
+Functionality for dealing with legends on plots
+"""
+from __future__ import (absolute_import, division, print_function)
+
+import sys
+
+import matplotlib
+from matplotlib import colors
+from matplotlib.patches import BoxStyle
+
+from mantid.plots.utility import legend_set_draggable
+
+
+def convert_color_to_hex(color):
+    """Convert a matplotlib color to its hex form"""
+    try:
+        return colors.cnames[color]
+    except (KeyError, TypeError):
+        rgb = colors.colorConverter.to_rgb(color)
+        return colors.rgb2hex(rgb)
+
+
+class LegendProperties(dict):
+    def __getattr__(self, item):
+        return self[item]
+
+    @classmethod
+    def from_legend(cls, legend):
+        props = dict()
+
+        props['visible'] = legend.get_visible()
+
+        title = legend.get_title()
+        if sys.version_info[0] >= 3:
+            if isinstance(title.get_text(), str):
+                props['title'] = title.get_text()
+            else:
+                props['title'] = None
+        else:
+            if isinstance(title.get_text(), unicode):
+                props['title'] = title.get_text()
+            else:
+                props['title'] = None
+
+        props['title_font'] = title.get_fontname()
+        props['title_size'] = title.get_fontsize()
+        props['title_color'] = convert_color_to_hex(title.get_color())
+
+        props['box_visible'] = legend.get_frame_on()
+
+        box = legend.get_frame()
+        props['background_color'] = convert_color_to_hex(box.get_facecolor())
+        props['edge_color'] = convert_color_to_hex(box.get_edgecolor())
+        props['transparency'] = box.get_alpha()
+
+        text = legend.get_texts()[0]
+        props['entries_font'] = text.get_fontname()
+        props['entries_size'] = text.get_fontsize()
+        props['entries_color'] = convert_color_to_hex(text.get_color())
+
+        props['marker_size'] = legend.handlelength
+        props['shadow'] = legend.shadow
+
+        boxstyle = legend.legendPatch.get_boxstyle()
+        if isinstance(boxstyle, BoxStyle.Round):
+            props['round_edges'] = True
+        else:
+            props['round_edges'] = False
+
+        props['columns'] = legend._ncol
+        props['column_spacing'] = legend.columnspacing
+        props['label_spacing'] = legend.labelspacing
+
+        position = legend._legend_handle_box.get_children()[0].align
+        if position == "baseline":
+            props['marker_position'] = "Left of Entries"
+        else:
+            props['marker_position'] = "Right of Entries"
+
+        props['markers'] = legend.numpoints
+        props['border_padding'] = legend.borderpad
+        props['marker_label_padding'] = legend.handletextpad
+
+        return cls(props)
+
+    @classmethod
+    def from_view(cls, view):
+        props = dict()
+        props['visible'] = not view.hide_legend_check_box.isChecked()
+        props['title'] = view.get_title()
+        props['background_color'] = view.get_background_color()
+        props['edge_color'] = view.get_edge_color()
+        props['transparency'] = (100-float(view.get_transparency_spin_box_value()))/100
+        props['entries_font'] = view.get_entries_font()
+        props['entries_size'] = view.get_entries_size()
+        props['entries_color'] = view.get_entries_color()
+        props['title_font'] = view.get_title_font()
+        props['title_size'] = view.get_title_size()
+        props['title_color'] = view.get_title_color()
+        props['marker_size'] = view.get_marker_size()
+        props['box_visible'] = not view.get_hide_box()
+        return cls(props)
+
+    @classmethod
+    def from_view_advanced(cls, view):
+        props = dict()
+        props['shadow'] = view.get_shadow()
+        props['round_edges'] = view.get_round_edges()
+        props['columns'] = view.get_number_of_columns()
+        props['column_spacing'] = view.get_column_spacing()
+        props['label_spacing'] = view.get_label_spacing()
+        props['marker_position'] = view.get_marker_position()
+        props['markers'] = view.get_number_of_markers()
+        props['border_padding'] = view.get_border_padding()
+        props['marker_label_padding'] = view.get_marker_label_padding()
+        return cls(props)
+
+    @classmethod
+    def create_legend(cls, props, ax):
+        if int(matplotlib.__version__[0]) >= 2:
+            legend = ax.legend(ncol=props['columns'],
+                               prop={'size': props['entries_size']},
+                               numpoints=props['markers'],
+                               markerfirst=props['marker_position'] == "Left of Entries",
+                               frameon=props['box_visible'],
+                               fancybox=props['round_edges'],
+                               shadow=props['shadow'],
+                               framealpha=props['transparency'],
+                               facecolor=props['background_color'],
+                               edgecolor=props['edge_color'],
+                               title=props['title'],
+                               borderpad=props['border_padding'],
+                               labelspacing=props['label_spacing'],
+                               handlelength=props['marker_size'],
+                               handletextpad=props['marker_label_padding'],
+                               columnspacing=props['column_spacing'])
+        else:
+            legend = ax.legend(ncol=props['columns'],
+                               prop={'size': props['entries_size']},
+                               numpoints=props['markers'],
+                               markerfirst=props['marker_position'] == "Left of Entries",
+                               frameon=props['box_visible'],
+                               fancybox=props['round_edges'],
+                               shadow=props['shadow'],
+                               framealpha=props['transparency'],
+                               title=props['title'],
+                               borderpad=props['border_padding'],
+                               labelspacing=props['label_spacing'],
+                               handlelength=props['marker_size'],
+                               handletextpad=props['marker_label_padding'],
+                               columnspacing=props['column_spacing'])
+
+        title = legend.get_title()
+        title.set_fontname(props['title_font'])
+        title.set_fontsize(props['title_size'])
+        title.set_color(props['title_color'])
+
+        for text in legend.get_texts():
+            text.set_fontname(props['entries_font'])
+            text.set_fontsize(props['entries_size'])
+            text.set_color(props['entries_color'])
+
+        legend.set_visible(props['visible'])
+        legend_set_draggable(legend, True)
diff --git a/MantidPlot/CMakeLists.txt b/MantidPlot/CMakeLists.txt
index 394f3d0978aa7a4a17082783172f362e8021efc7..1a778bbf120bb48a9ba8aa87ae7e7da71efa3957 100644
--- a/MantidPlot/CMakeLists.txt
+++ b/MantidPlot/CMakeLists.txt
@@ -420,7 +420,6 @@ include(UseQt4)
 # Do the sip generation.
 #
 
-include_directories(${PYTHON_INCLUDE_PATH})
 
 # We need to manually add all the headers that are in qti.sip so that the
 # dependencies are known to CMake
@@ -459,21 +458,23 @@ set(MANTIDQTPYTHON_SIP_INCLUDES
     ${CMAKE_CURRENT_BINARY_DIR}/../qt/python/mantidqtpython
     -I
     ${CMAKE_CURRENT_SOURCE_DIR}/../qt/python/mantidqtpython)
+if ( USE_PRIVATE_SIPPYQT4 )
+  set ( _sip_include_dir ${PYQT4_SIP_INCLUDE_DIR} )
+  set ( _sip_executable ${PYQT4_SIP_EXECUTABLE} )
+else ()
+  set ( _sip_include_dir ${SIP_INCLUDE_DIR} )
+  set ( _sip_executable ${SIP_EXECUTABLE} )
+endif ()
+
 add_custom_command(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${SIP_SRC_AUTO}
-                   COMMAND ${SIP_EXECUTABLE} -I ${PYQT4_SIP_DIR}
+                   COMMAND ${_sip_executable} -I ${PYQT4_SIP_DIR}
                            ${MANTIDQTPYTHON_SIP_INCLUDES} ${PYQT4_SIP_FLAGS} -c
                            ${CMAKE_CURRENT_BINARY_DIR} -j1 -w -o ${SIP_SPEC}
-                   DEPENDS ${SIP_INCLUDE_DIR}/sip.h
-                           src/qti.sip
+                   DEPENDS src/qti.sip
                            ${QTI_SIP_HDRS}
                            ../qt/python/mantidqtpython/mantidqtpython_def.sip
                    COMMENT "Generating python bindings using sip")
 
-# Needed for sip.h header that can end up in a different place to to the main
-# Python include directory
-include_directories(SYSTEM ${SIP_INCLUDE_DIR})
-# Needed for sip generated files to find includes in src
-include_directories(${CMAKE_CURRENT_SOURCE_DIR})
 
 #
 # Specify the files that we need to pass to Qt macros
@@ -671,9 +672,6 @@ enable_precompiled_headers(src/PrecompiledHeader.h ALL_SRC)
 
 qt4_wrap_ui(UI_HDRS ${UI_FILES})
 
-# The generated ui headers will go here:
-include_directories(${CMAKE_CURRENT_BINARY_DIR})
-
 #
 # Internal icon links
 #
@@ -687,19 +685,16 @@ qt4_add_resources(RES_FILES
 #
 # Add the dependencies
 #
-
+# This must come before ParaView so Python gets picked up from our third-party on Windows
+# and not the one Paraview is built with. Warnings from our external repo are all ignored
+# but the ones built by ParaView are not.
+include_directories(SYSTEM ${_sip_include_dir} ${PYTHON_INCLUDE_PATH})
 if(MAKE_VATES)
   include(${PARAVIEW_USE_FILE})
 endif()
 
-include_directories(SYSTEM ${MUPARSER_INCLUDE_DIR})
-include_directories(${ZLIB_INCLUDE_DIRS})
-
-include_directories(SYSTEM ${QWT5_INCLUDE_DIR})
 
 find_package(QwtPlot3d REQUIRED)
-include_directories(SYSTEM ${QWTPLOT3D_INCLUDE_DIR})
-
 find_package(QScintillaQt4 REQUIRED)
 
 #
@@ -801,20 +796,31 @@ add_executable(MantidPlot
                ${CONFIG_RESET_SCRIPT_FILE}
                ${IPYTHON_INSTALL_FILES})
 
-target_include_directories(MantidPlot
-                           PRIVATE ${SIP_INCLUDE_DIR}
-			           src
-                                   src/lib/include
-                                   src/lib/3rdparty/qtcolorpicker/src
-                                   ../qt/widgets/common/inc
-                                   ../qt/widgets/instrumentview/inc
-                                   ../qt/widgets/sliceviewer/inc
-                                   ../qt/widgets/spectrumviewer/inc
-                                   ../qt/widgets/factory/inc
-                                   ../Framework/PythonInterface/inc)
+
+# Sip must be before Python for the case of our private sip module for RHEL
+# so that it gets picked up before the system one if it exists
+target_include_directories(MantidPlot SYSTEM PRIVATE
+  ${MUPARSER_INCLUDE_DIR}
+  ${ZLIB_INCLUDE_DIRS}
+  ${QWT5_INCLUDE_DIR}
+  ${QWTPLOT3D_INCLUDE_DIR}
+)
+target_include_directories(MantidPlot PRIVATE
+  ${CMAKE_CURRENT_BINARY_DIR}
+  ${CMAKE_CURRENT_SOURCE_DIR}
+  src
+  src/lib/include
+  src/lib/3rdparty/qtcolorpicker/src
+  ../qt/widgets/common/inc
+  ../qt/widgets/instrumentview/inc
+  ../qt/widgets/sliceviewer/inc
+  ../qt/widgets/spectrumviewer/inc
+  ../qt/widgets/factory/inc
+  ../Framework/PythonInterface/inc
+)
 
 # GCC 8 onwards needs to disable functional casting at the Python interface
-target_compile_options( MantidPlot PRIVATE 
+target_compile_options( MantidPlot PRIVATE
   $<$<AND:$<CXX_COMPILER_ID:GNU>,$<VERSION_GREATER_EQUAL:$<CXX_COMPILER_VERSION>,8.0>>:-Wno-cast-function-type> )
 
 # Library dependencies
diff --git a/buildconfig/CMake/DetermineLinuxDistro.cmake b/buildconfig/CMake/DetermineLinuxDistro.cmake
index 52936753319450a50bf2bcc123eaa81f99f1fa6a..5aad0c9e9cc0ee5f23f2ecc0529545e3f9699d31 100644
--- a/buildconfig/CMake/DetermineLinuxDistro.cmake
+++ b/buildconfig/CMake/DetermineLinuxDistro.cmake
@@ -9,7 +9,7 @@ if ( LSB_CMD )
   string ( STRIP ${UNIX_DIST} UNIX_DIST )
   string ( REGEX REPLACE "RedHatEnterpriseClient" "RedHatEnterprise" UNIX_DIST ${UNIX_DIST} )
   string ( REGEX REPLACE "RedHatEnterpriseWorkstation" "RedHatEnterprise" UNIX_DIST ${UNIX_DIST} )
-  # Make Scientific Linux and CentOS look like RHEL6
+  # Make Scientific Linux and CentOS look like RHEL
   string ( REGEX REPLACE "Scientific" "RedHatEnterprise" UNIX_DIST ${UNIX_DIST} )
   string ( REGEX REPLACE "CentOS" "RedHatEnterprise" UNIX_DIST ${UNIX_DIST} )
   # get the codename
diff --git a/buildconfig/CMake/ExternalSipPyQt4.cmake b/buildconfig/CMake/ExternalSipPyQt4.cmake
new file mode 100644
index 0000000000000000000000000000000000000000..2aece3fb4f755a129f00183c2d892e7fc098263c
--- /dev/null
+++ b/buildconfig/CMake/ExternalSipPyQt4.cmake
@@ -0,0 +1,82 @@
+include(ExternalProject)
+include(ProcessorCount)
+
+set(_SIP_PYQT_DIR extern-pyt4-sip)
+set(_SIP_PYQT_INSTALL_DIR ${_SIP_PYQT_DIR}/install)
+
+# sipdir - use different variables to standard sip installation to be able to distingish it
+set(PYQT4_SIP_INCLUDE_DIR "${CMAKE_BINARY_DIR}/${_SIP_PYQT_INSTALL_DIR}/include"  CACHE STRING "sip include directory" FORCE)
+set(PYQT4_SIP_EXECUTABLE "${CMAKE_BINARY_DIR}/${_SIP_PYQT_INSTALL_DIR}/bin/sip" CACHE STRING "sip executable" FORCE)
+set(PYQT4_SIP_VERSION "041307" CACHE STRING "sip hexadecimal string" FORCE)
+set(PYQT4_SIP_VERSION_STR "4.19.7" CACHE STRING "sip version string" FORCE)
+ExternalProject_Add(extern-pyqt4-sip
+  PREFIX ${_SIP_PYQT_DIR}/sip
+  INSTALL_DIR ${_SIP_PYQT_INSTALL_DIR}
+  URL https://www.riverbankcomputing.com/static/Downloads/sip/4.19.7/sip-4.19.7.tar.gz
+  URL_HASH MD5=ae4f2db79713046d61b2a44e5ee1e3ab
+  CONFIGURE_COMMAND "${PYTHON_EXECUTABLE}" "<SOURCE_DIR>/configure.py"
+    --sip-module=PyQt4.sip
+    --bindir=<INSTALL_DIR>/bin
+    --destdir=<INSTALL_DIR>/lib/site-packages
+    --incdir=<INSTALL_DIR>/include
+    --sipdir=<INSTALL_DIR>/share/sip
+  BUILD_COMMAND make 2> build.log
+)
+
+# PyQt4
+set(PYQT4_VERSION "040c01" CACHE STRING "PyQt4's version as a 6-digit hexadecimal number" FORCE)
+set(PYQT4_VERSION_STR "4.12.1" CACHE STRING "PyQt4's version as a human-readable string" FORCE)
+set(PYQT4_VERSION_TAG "Qt_4_8_6" CACHE STRING "The Qt version tag used by PyQt4's .sip files" FORCE)
+set(PYQT4_SIP_DIR "${CMAKE_BINARY_DIR}/${_SIP_PYQT_INSTALL_DIR}/share/sip" CACHE PATH "The base directory where PyQt4's .sip files are installed" FORCE)
+set(PYQT4_SIP_FLAGS "-x VendorID -t WS_X11 -x PyQt_NoPrintRangeBug -t Qt_4_8_6" CACHE STRING "The SIP flags used to build PyQt4" FORCE)
+set(PRIVATE_PYQT_SITE_PACKAGES ${CMAKE_BINARY_DIR}/${_SIP_PYQT_INSTALL_DIR}/lib/site-packages)
+set(_pyqt4_lib_site_packages ${PRIVATE_PYQT_SITE_PACKAGES}/PyQt4)
+
+# Write a wrapper pyuic script so it can find out internal copy of PyQt4
+set(PYQT4_PYUIC "${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/internal-pyuic.py" CACHE STRING "Location of the pyuic script" FORCE)
+configure_file(${CMAKE_MODULE_PATH}/internal-pyuic.py.in ${PYQT4_PYUIC} @ONLY)
+
+# Determine core count for make step
+ProcessorCount(NPROCESSORS)
+if(NPROCESSORS EQUAL 0)
+  set(NPROCESSORS 1)
+endif()
+
+ExternalProject_Add(extern-pyqt4
+  PREFIX ${_SIP_PYQT_DIR}/pyqt4
+  INSTALL_DIR ${_SIP_PYQT_INSTALL_DIR}
+  URL https://sourceforge.net/projects/pyqt/files/PyQt4/PyQt-4.12.1/PyQt4_gpl_x11-4.12.1.tar.gz/download
+  URL_HASH MD5=0112e15858cd7d318a09e7366922f874
+  PATCH_COMMAND patch -p1 --input ${CMAKE_SOURCE_DIR}/buildconfig/CMake/pyqt4_qreal_float_support.patch
+    COMMAND patch -p0 --input ${CMAKE_SOURCE_DIR}/buildconfig/CMake/pyqt4_disable_unnecessary_modules.patch
+    # patch configure to pick up sipconfig built above
+    COMMAND sed -i -e "/^import sipconfig/i sys.path.insert(0, \"${_pyqt4_lib_site_packages}\")" "<SOURCE_DIR>/configure.py"
+  CONFIGURE_COMMAND "${PYTHON_EXECUTABLE}" "<SOURCE_DIR>/configure.py"
+    --assume-shared
+    --confirm-license
+    --bindir=<INSTALL_DIR>/bin
+    --destdir=<INSTALL_DIR>/lib/site-packages
+    --sipdir=<INSTALL_DIR>/share/sip
+    --no-designer-plugin
+    --no-timestamp
+    --no-deprecated
+    --qmake=/usr/bin/qmake-qt4
+    --no-qsci-api
+  BUILD_COMMAND make -j${NPROCESSORS} 2> build.log
+  DEPENDS extern-pyqt4-sip
+)
+
+# Write out .pth file to find this. We ensure to insert our path ahead of others so it takes precendence over the system
+# We assume we only have to support a single-configuration build type on Linux
+file(WRITE ${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/private-pyqt4.pth
+"import sys; sys.__plen = len(sys.path)
+${PRIVATE_PYQT_SITE_PACKAGES}
+${_pyqt4_lib_site_packages}
+import sys; new = sys.path[sys.__plen:]; del sys.path[sys.__plen:]; p = getattr(sys, '__egginsert', 0); sys.path[p:p] = new; sys.__egginsert = p + len(new)
+")
+
+# Package PyQt. We assume this is for Python 3
+install(DIRECTORY ${PRIVATE_PYQT_SITE_PACKAGES}/PyQt4
+        DESTINATION ${LIB_DIR} 
+        PATTERN "__pycache__" EXCLUDE
+        PATTERN "port_v2" EXCLUDE)
diff --git a/buildconfig/CMake/FindPyQt.py b/buildconfig/CMake/FindPyQt.py
index 0da4e37568ef76f4df076577ef45164f8ff502e1..b25705f7c35b731171dd2c48885f807338bed77c 100644
--- a/buildconfig/CMake/FindPyQt.py
+++ b/buildconfig/CMake/FindPyQt.py
@@ -45,15 +45,18 @@ class PyQtConfig(object):
               raise RuntimeError("Unknown Qt version ({}) found. Unable to determine location of PyQt sip files."
                                  "Please update FindPyQt accordingly.".format(self.version_str[0]))
       else:
-          # RHEL has a separate python2-sip and python3-sip directory
+          # RH/Fedora have separate pythonX-sip or pythonXY-sip  directories
           prefix_share = os.path.join(sys.prefix, 'share')
-          possible_sip_dirs = (os.path.join(prefix_share, 'sip', name),
-                               os.path.join(prefix_share,
-                                           'python{}-sip'.format(sys.version_info.major),
-                                            name))
+          possible_sip_dirs = (
+              'python{}{}-sip'.format(sys.version_info.major, sys.version_info.minor),
+              'python{}-sip'.format(sys.version_info.major),
+              'sip'
+          )
           for sip_dir in possible_sip_dirs:
-              if os.path.exists(sip_dir):
-                  self.sip_dir = sip_dir
+              pyqt_sip_dir = os.path.join(prefix_share, sip_dir, name)
+              if os.path.exists(pyqt_sip_dir):
+                  self.sip_dir = pyqt_sip_dir
+                  break
 
       # Assume uic script is in uic submodule
       uic = __import__(name + '.uic', globals(), locals(), ['uic'], 0)
diff --git a/buildconfig/CMake/LinuxPackageScripts.cmake b/buildconfig/CMake/LinuxPackageScripts.cmake
index 5ea3e8c70b0a38fd87e02ed1e08e3365bc40f577..212664bbc679c1f778022f93b5d2a453852a398b 100644
--- a/buildconfig/CMake/LinuxPackageScripts.cmake
+++ b/buildconfig/CMake/LinuxPackageScripts.cmake
@@ -30,6 +30,9 @@ if ( CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT )
   set ( CMAKE_INSTALL_PREFIX /opt/mantid${CPACK_PACKAGE_SUFFIX} CACHE PATH "Install path" FORCE )
 endif()
 
+# Tell rpm to use the appropriate python executable
+set(CPACK_RPM_SPEC_MORE_DEFINE "%define __python ${PYTHON_EXECUTABLE}")
+
 # Tell rpm that this package does not own /opt /usr/share/{applications,pixmaps}
 # Required for Fedora >= 18 and RHEL >= 7
 set ( CPACK_RPM_EXCLUDE_FROM_AUTO_FILELIST_ADDITION /opt /usr/share/applications /usr/share/pixmaps )
@@ -69,6 +72,7 @@ print(sc.get_python_lib(plat_specific=True))"
 
 file ( WRITE ${CMAKE_CURRENT_BINARY_DIR}/mantid.pth.install
   "${CMAKE_INSTALL_PREFIX}/${BIN_DIR}\n"
+  "${CMAKE_INSTALL_PREFIX}/${LIB_DIR}\n"
 )
 
 install ( FILES ${CMAKE_CURRENT_BINARY_DIR}/mantid.pth.install
diff --git a/buildconfig/CMake/Packaging/launch_mantidplot.sh.in b/buildconfig/CMake/Packaging/launch_mantidplot.sh.in
index 8f9a573c6bc3679f30059feaa4da16dd217d8e4f..ea5054cd8d3f5faab24db9c8619e25cf052f551d 100644
--- a/buildconfig/CMake/Packaging/launch_mantidplot.sh.in
+++ b/buildconfig/CMake/Packaging/launch_mantidplot.sh.in
@@ -19,6 +19,11 @@ if [ -n "${PYTHONPATH}" ]; then
     LOCAL_PYTHONPATH=${LOCAL_PYTHONPATH}:${PYTHONPATH}
 fi
 
+# Find private sip module if it exists. Required by qtpy & IPython/external/qt_loader.py
+if [ -f "${INSTALLDIR}/lib/PyQt4/sip.so" ]; then
+    LOCAL_PYTHONPATH=$LOCAL_PYTHONPATH:${INSTALLDIR}/lib/PyQt4
+fi
+
 @GDB_DEFINITIONS@
 
 # Launch
diff --git a/buildconfig/CMake/Packaging/mantidpython.in b/buildconfig/CMake/Packaging/mantidpython.in
index 1d4346bb3a8cc6987d6fcad7eb09f52e9faf74f5..fcda7c7e0e008ddef2c8da4f11380be7ef3e2458 100755
--- a/buildconfig/CMake/Packaging/mantidpython.in
+++ b/buildconfig/CMake/Packaging/mantidpython.in
@@ -20,7 +20,7 @@ fi
 PV_PLUGIN_PATH="${INSTALLDIR}/plugins/paraview/qt4"
 
 # Define where python libraries are
-LOCAL_PYTHONPATH=${INSTALLDIR}/bin:@LOCAL_PYPATH@@PARAVIEW_PYTHON_PATHS@
+LOCAL_PYTHONPATH=@LOCAL_PYPATH@@PARAVIEW_PYTHON_PATHS@
 if [ -n "${PYTHONPATH}" ]; then
     LOCAL_PYTHONPATH=${LOCAL_PYTHONPATH}:${PYTHONPATH}
 fi
@@ -32,6 +32,13 @@ else
     LOCAL_QT_API="pyqt" # force to use qt4
 fi
 
+# Find private sip module if it exists. Required by qtpy
+if [ -f "${INSTALLDIR}/lib/PyQt4/sip.so" ]; then
+  if [ "$LOCAL_QT_API" = "pyqt" ] || [ "$LOCAL_QT_API" = "pyqt4" ]; then
+    LOCAL_PYTHONPATH=$LOCAL_PYTHONPATH:${INSTALLDIR}/lib/PyQt4
+  fi
+fi
+
 if [ -n "$1" ] && [ "$1" = "--classic" ]; then
     shift
     set -- @WRAPPER_PREFIX@@PYTHON_EXECUTABLE@ @PYTHON_ARGS@ "$@"@WRAPPER_POSTFIX@
diff --git a/buildconfig/CMake/PyQtFindImpl.cmake b/buildconfig/CMake/PyQtFindImpl.cmake
index 3bfcf7a66f6bd20e4afc438f5f0641c21d347bcd..f75cce7b16b56b70cd3967fd5ebb1bd170ccda2b 100644
--- a/buildconfig/CMake/PyQtFindImpl.cmake
+++ b/buildconfig/CMake/PyQtFindImpl.cmake
@@ -27,6 +27,7 @@ function (find_pyqt major_version)
   if (NOT EXISTS ${_find_pyqt_py})
     message(FATAL_ERROR "Failed to find FindPyQt.py in \"${CMAKE_MODULE_PATH}\"")
   endif()
+
   execute_process (COMMAND ${PYTHON_EXECUTABLE} ${_find_pyqt_py} ${major_version}
     OUTPUT_VARIABLE _pyqt_config ERROR_VARIABLE _pyqt_config_err)
   if(CMAKE_HOST_WIN32 AND major_version EQUAL 4 AND _pyqt_config_err MATCHES "Qt: Untested Windows version 10.0 detected!")
diff --git a/buildconfig/CMake/SipQtTargetFunctions.cmake b/buildconfig/CMake/SipQtTargetFunctions.cmake
index 610c776bc56f7b820ca1c57341ec42b83cae5803..e3f73b4bbb112d190709d2e4b18994fe838d31e9 100644
--- a/buildconfig/CMake/SipQtTargetFunctions.cmake
+++ b/buildconfig/CMake/SipQtTargetFunctions.cmake
@@ -65,6 +65,14 @@ function ( mtd_add_sip_module )
   endforeach ()
 
   # Run sip code generator
+  if ( PARSED_PYQT_VERSION EQUAL 4 AND USE_PRIVATE_SIPPYQT4 )
+    set ( _sip_include_dir ${PYQT4_SIP_INCLUDE_DIR} )
+    set ( _sip_executable ${PYQT4_SIP_EXECUTABLE} )
+  else ()
+    set ( _sip_include_dir ${SIP_INCLUDE_DIR} )
+    set ( _sip_executable ${SIP_EXECUTABLE} )
+  endif ()
+
   set ( _module_spec ${CMAKE_CURRENT_BINARY_DIR}/${PARSED_MODULE_NAME}.sip )
   configure_file ( ${_sipmodule_template_path} ${_module_spec} )
   set ( _pyqt_sip_dir ${PYQT${PARSED_PYQT_VERSION}_SIP_DIR} )
@@ -73,10 +81,10 @@ function ( mtd_add_sip_module )
   set ( _pyqt_sip_flags ${PYQT${PARSED_PYQT_VERSION}_SIP_FLAGS} )
   set ( _sip_generated_cpp ${CMAKE_CURRENT_BINARY_DIR}/sip${PARSED_MODULE_NAME}part0.cpp )
   add_custom_command ( OUTPUT ${_sip_generated_cpp}
-    COMMAND ${PYTHON_EXECUTABLE} ${_sip_wrapper} ${SIP_EXECUTABLE}
+    COMMAND ${PYTHON_EXECUTABLE} ${_sip_wrapper} ${_sip_executable}
       ${_sip_include_flags} ${_pyqt_sip_flags}
       -c ${CMAKE_CURRENT_BINARY_DIR} -j1 -w -e ${_module_spec}
-    DEPENDS ${_module_spec} ${_sip_include_deps} ${SIP_INCLUDE_DIR}/sip.h
+    DEPENDS ${_module_spec} ${_sip_include_deps}
     COMMENT "Generating ${PARSED_MODULE_NAME} python bindings with sip"
   )
 
@@ -84,13 +92,16 @@ function ( mtd_add_sip_module )
   # Suppress Warnings about sip bindings have PyObject -> PyFunc casts which
   # is a valid pattern GCC8 onwards detects
   # GCC 8 onwards needs to disable functional casting at the Python interface
-  target_compile_options( ${PARSED_TARGET_NAME} PRIVATE 
+  target_compile_options( ${PARSED_TARGET_NAME} PRIVATE
     $<$<AND:$<CXX_COMPILER_ID:GNU>,$<VERSION_GREATER_EQUAL:$<CXX_COMPILER_VERSION>,8.0>>:-Wno-cast-function-type> )
-  target_include_directories ( ${PARSED_TARGET_NAME} SYSTEM PRIVATE ${SIP_INCLUDE_DIR} )
+  target_include_directories ( ${PARSED_TARGET_NAME} SYSTEM PRIVATE ${_sip_include_dir} ${PYTHON_INCLUDE_PATH})
   target_include_directories ( ${PARSED_TARGET_NAME} PRIVATE ${PARSED_INCLUDE_DIRS} )
   target_include_directories ( ${PARSED_TARGET_NAME} SYSTEM PRIVATE ${PARSED_SYSTEM_INCLUDE_DIRS} )
   target_link_libraries ( ${PARSED_TARGET_NAME} PRIVATE ${PARSED_LINK_LIBS} )
 
+  if ( PARSED_PYQT_VERSION EQUAL 4 AND USE_PRIVATE_SIPPYQT4 )
+    add_dependencies( ${PARSED_TARGET_NAME} extern-pyqt4 )
+  endif ()
   # Set all required properties on the target
   set_target_properties ( ${PARSED_TARGET_NAME} PROPERTIES
     LIBRARY_OUTPUT_NAME ${PARSED_MODULE_NAME} )
diff --git a/buildconfig/CMake/UiToPy.cmake b/buildconfig/CMake/UiToPy.cmake
index 5793bcad87d73117007e96469fb0d6f187914f6d..e350af0904a99a59eab271dff9e3612596c5850e 100644
--- a/buildconfig/CMake/UiToPy.cmake
+++ b/buildconfig/CMake/UiToPy.cmake
@@ -4,7 +4,6 @@
 
 
 function(UiToPy ui_files target_name)
-
   set(py_exec ${PYTHON_EXECUTABLE})
   set(py_uic_py ${PYQT4_PYUIC} ) # From FindPyQt4
   set(ui_dir ${CMAKE_CURRENT_SOURCE_DIR})
@@ -17,13 +16,14 @@ function(UiToPy ui_files target_name)
     # Source file to generate from
     set( source_file ${ui_dir}/${ui_name}.ui )
     # Command to run the translation
-    add_custom_command(OUTPUT ${generated_file} COMMAND ${py_exec} ${py_uic_py} ${source_file} -o ${generated_file} COMMAND ${py_exec} ${CMAKE_SOURCE_DIR}/buildconfig/wrap_pyui.py ${generated_file} DEPENDS ${source_file}) 
+    add_custom_command(OUTPUT ${generated_file} COMMAND ${py_exec} ${py_uic_py} ${source_file} -o ${generated_file} COMMAND ${py_exec} ${CMAKE_SOURCE_DIR}/buildconfig/wrap_pyui.py ${generated_file} DEPENDS ${source_file})
     # Record all generated files
     list(APPEND _outputs ${generated_file})
 
   endforeach(ui_file)
   # Create a custom target
   add_custom_target(${target_name} DEPENDS ${_outputs})
-  
+  if(USE_PRIVATE_SIPPYQT4)
+    add_dependencies(${target_name} extern-pyqt4)
+  endif()
 endfunction(UiToPy)
-
diff --git a/buildconfig/CMake/internal-pyuic.py.in b/buildconfig/CMake/internal-pyuic.py.in
new file mode 100644
index 0000000000000000000000000000000000000000..6cd825792ca9ea5004688ad29c9ddf97d907e6a1
--- /dev/null
+++ b/buildconfig/CMake/internal-pyuic.py.in
@@ -0,0 +1,3 @@
+import sys
+sys.path.insert(0, "@PRIVATE_PYQT_SITE_PACKAGES@")
+import PyQt4.uic.pyuic
diff --git a/buildconfig/CMake/pyqt4_disable_unnecessary_modules.patch b/buildconfig/CMake/pyqt4_disable_unnecessary_modules.patch
new file mode 100644
index 0000000000000000000000000000000000000000..0b524720bf727afdb8d7326e883e241ccbb14f00
--- /dev/null
+++ b/buildconfig/CMake/pyqt4_disable_unnecessary_modules.patch
@@ -0,0 +1,94 @@
+--- configure.py.orig	2020-01-09 15:07:19.231636923 +0000
++++ configure.py	2020-01-09 15:10:28.159688544 +0000
+@@ -1,19 +1,19 @@
+ # This script generates the PyQt configuration and generates the Makefiles.
+ #
+ # Copyright (c) 2016 Riverbank Computing Limited <info@riverbankcomputing.com>
+-# 
++#
+ # This file is part of PyQt4.
+-# 
++#
+ # This file may be used under the terms of the GNU General Public License
+ # version 3.0 as published by the Free Software Foundation and appearing in
+ # the file LICENSE included in the packaging of this file.  Please review the
+ # following information to ensure the GNU General Public License version 3.0
+ # requirements will be met: http://www.gnu.org/copyleft/gpl.html.
+-# 
++#
+ # If you do not wish to use this file under the terms of the GPL version 3.0
+ # then you may purchase a commercial license.  For more information contact
+ # info@riverbankcomputing.com.
+-# 
++#
+ # This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
+ # WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ 
+@@ -90,7 +90,7 @@ def find_default_qmake():
+ 
+     for d in path.split(os.pathsep):
+         qmake = os.path.join(d, base_qmake)
+-  
++
+         if os.access(qmake, os.X_OK):
+             return qmake
+ 
+@@ -109,7 +109,7 @@ def create_optparser():
+         if not os.path.isdir(value):
+             raise optparse.OptionValueError("'%s' is not a directory" % value)
+         setattr(parser.values, option.dest, os.path.abspath(value))
+-        
++
+     def store_abspath_file(option, opt_str, value, parser):
+         if not os.path.isfile(value):
+             raise optparse.OptionValueError("'%s' is not a file" % value)
+@@ -342,8 +342,8 @@ class ConfigurePyQt4:
+ 
+         check_module("QtGui", "qwidget.h", "new QWidget()")
+         check_module("QtHelp", "qhelpengine.h", "new QHelpEngine(\"foo\")")
+-        check_module("QtMultimedia", "QAudioDeviceInfo",
+-                "new QAudioDeviceInfo()")
++        # check_module("QtMultimedia", "QAudioDeviceInfo",
++        #         "new QAudioDeviceInfo()")
+         check_module("QtNetwork", "qhostaddress.h", "new QHostAddress()")
+ 
+         # Qt v4.7 was current when we added support for QtDBus and we didn't
+@@ -364,14 +364,14 @@ class ConfigurePyQt4:
+                 extra_libs=sql_libs)
+         check_module("QtSvg", "qsvgwidget.h", "new QSvgWidget()")
+         check_module("QtTest", "QtTest", "QTest::qSleep(0)")
+-        check_module("QtWebKit", "qwebpage.h", "new QWebPage()")
++        # check_module("QtWebKit", "qwebpage.h", "new QWebPage()")
+         check_module("QtXml", "qdom.h", "new QDomDocument()")
+         check_module("QtXmlPatterns", "qxmlname.h", "new QXmlName()")
+         check_module("phonon", "phonon/videowidget.h",
+                 "new Phonon::VideoWidget()")
+-        check_module("QtAssistant", "qassistantclient.h",
+-                "new QAssistantClient(\"foo\")", extra_lib_dirs=ass_lib_dirs,
+-                extra_libs=ass_libs)
++        # check_module("QtAssistant", "qassistantclient.h",
++        #         "new QAssistantClient(\"foo\")", extra_lib_dirs=ass_lib_dirs,
++        #         extra_libs=ass_libs)
+ 
+         if qt_shared == '':
+             sipconfig.inform("QtDesigner module disabled with static Qt libraries.")
+@@ -503,8 +503,8 @@ class ConfigurePyQt4:
+         if "QtTest" in pyqt_modules:
+             generate_code("QtTest")
+ 
+-        if "QtWebKit" in pyqt_modules:
+-            generate_code("QtWebKit")
++        # if "QtWebKit" in pyqt_modules:
++        #     generate_code("QtWebKit")
+ 
+         if "QtXml" in pyqt_modules:
+             generate_code("QtXml")
+@@ -1511,7 +1511,7 @@ def needed_qt_libs(mname, qt_libs):
+         "QtSql": ["QtGui"],
+         "QtSvg": ["QtGui"],
+         "QtTest": ["QtGui"],
+-        "QtWebKit": ["QtNetwork", "QtGui"],
++        # "QtWebKit": ["QtNetwork", "QtGui"],
+         "QtXml": ["QtCore"],
+         "QtXmlPatterns": ["QtNetwork", "QtCore"],
+         "phonon": ["QtGui"],
diff --git a/buildconfig/CMake/pyqt4_qreal_float_support.patch b/buildconfig/CMake/pyqt4_qreal_float_support.patch
new file mode 100644
index 0000000000000000000000000000000000000000..d8c3ef74723e77f9ee28ca8303ebf7d669dc6708
--- /dev/null
+++ b/buildconfig/CMake/pyqt4_qreal_float_support.patch
@@ -0,0 +1,240 @@
+From: Michael Casadevall <mcasadevall@debian.org>
+Date: Thu, 8 Oct 2015 12:56:35 -0700
+Subject: Add QList<double> support explicitly when qreal is not double
+
+---
+ sip/QtCore/qlist.sip | 224 +++++++++++++++++++++++++++++++++++++++++++++++++++
+ 1 file changed, 224 insertions(+)
+
+diff --git a/sip/QtCore/qlist.sip b/sip/QtCore/qlist.sip
+index 978e576..63002d2 100644
+--- a/sip/QtCore/qlist.sip
++++ b/sip/QtCore/qlist.sip
+@@ -814,3 +814,227 @@ template<qreal, TYPE>
+     return sipGetState(sipTransferObj);
+ %End
+ };
++
++// If we're on an architecture where qreal != double, then we need to also
++// explicately handle doubles. On architectures where qreal == double, they
++// will automaticially be cast upwards
++
++%If (!PyQt_qreal_double)
++
++%If (Qt_4_3_0 -)
++// QList<QPair<double, double> > is implemented as a Python list of 2-element tuples.
++%MappedType QList<QPair<double, double> >
++{
++%TypeHeaderCode
++#include <qlist.h>
++#include <qpair.h>
++%End
++
++%ConvertFromTypeCode
++    // Create the list.
++    PyObject *l;
++
++    if ((l = PyList_New(sipCpp->size())) == NULL)
++        return NULL;
++
++    // Set the list elements.
++    for (int i = 0; i < sipCpp->size(); ++i)
++    {
++        const QPair<double, double> &p = sipCpp->at(i);
++        PyObject *pobj;
++
++        if ((pobj = Py_BuildValue((char *)"dd", p.first, p.second)) == NULL)
++        {
++            Py_DECREF(l);
++
++            return NULL;
++        }
++
++        PyList_SET_ITEM(l, i, pobj);
++    }
++
++    return l;
++%End
++
++%ConvertToTypeCode
++    SIP_SSIZE_T len;
++
++    // Check the type if that is all that is required.
++    if (sipIsErr == NULL)
++    {
++        if (!PySequence_Check(sipPy) || (len = PySequence_Size(sipPy)) < 0)
++            return 0;
++
++        for (SIP_SSIZE_T i = 0; i < len; ++i)
++        {
++            PyObject *tup = PySequence_ITEM(sipPy, i);
++
++            if (!PySequence_Check(tup) || PySequence_Size(tup) != 2)
++                return 0;
++        }
++
++        return 1;
++    }
++
++    QList<QPair<double, double> > *ql = new QList<QPair<double, double> >;
++    len = PySequence_Size(sipPy);
++
++    for (SIP_SSIZE_T i = 0; i < len; ++i)
++    {
++        PyObject *tup = PySequence_ITEM(sipPy, i);
++
++        double first = PyFloat_AsDouble(PySequence_ITEM(tup, 0));
++        double second = PyFloat_AsDouble(PySequence_ITEM(tup, 1));
++
++        ql->append(QPair<double, double>(first, second));
++    }
++
++    *sipCppPtr = ql;
++
++    return sipGetState(sipTransferObj);
++%End
++};
++%End
++%If (Qt_4_3_0 -)
++// QList<QPair<double, TYPE> > is implemented as a Python list of 2-element tuples.
++template<double, TYPE>
++%MappedType QList<QPair<double, TYPE> >
++{
++%TypeHeaderCode
++#include <qlist.h>
++#include <qpair.h>
++%End
++
++%ConvertFromTypeCode
++    // Create the list.
++    PyObject *l;
++
++    if ((l = PyList_New(sipCpp->size())) == NULL)
++        return NULL;
++
++    // Set the list elements.
++    for (int i = 0; i < sipCpp->size(); ++i)
++    {
++        const QPair<double, TYPE> &p = sipCpp->at(i);
++        TYPE *t = new TYPE(p.second);
++        PyObject *pobj;
++
++        if ((pobj = sipBuildResult(NULL, "(dB)", p.first, t, sipClass_TYPE, sipTransferObj)) == NULL)
++        {
++            Py_DECREF(l);
++            delete t;
++
++            return NULL;
++        }
++
++        PyList_SET_ITEM(l, i, pobj);
++    }
++
++    return l;
++%End
++
++%ConvertToTypeCode
++    SIP_SSIZE_T len;
++
++    // Check the type if that is all that is required.
++    if (sipIsErr == NULL)
++    {
++        if (!PySequence_Check(sipPy) || (len = PySequence_Size(sipPy)) < 0)
++            return 0;
++
++        for (SIP_SSIZE_T i = 0; i < len; ++i)
++        {
++            PyObject *tup = PySequence_ITEM(sipPy, i);
++
++            if (!PySequence_Check(tup) || PySequence_Size(tup) != 2)
++                return 0;
++
++            if (!sipCanConvertToInstance(PySequence_ITEM(tup, 1), sipClass_TYPE, SIP_NOT_NONE))
++                return 0;
++        }
++
++        return 1;
++    }
++
++    QList<QPair<double, TYPE> > *ql = new QList<QPair<double, TYPE> >;
++    len = PySequence_Size(sipPy);
++
++    for (SIP_SSIZE_T i = 0; i < len; ++i)
++    {
++        PyObject *tup = PySequence_ITEM(sipPy, i);
++        double d;
++        int state;
++
++        d = PyFloat_AsDouble(PySequence_ITEM(tup, 0));
++        TYPE *t = reinterpret_cast<TYPE *>(sipConvertToInstance(PySequence_ITEM(tup, 1), sipClass_TYPE, sipTransferObj, SIP_NOT_NONE, &state, sipIsErr));
++
++        if (*sipIsErr)
++        {
++            sipReleaseInstance(t, sipClass_TYPE, state);
++
++            delete ql;
++            return 0;
++        }
++
++        ql->append(QPair<double, TYPE>(d, *t));
++
++        sipReleaseInstance(t, sipClass_TYPE, state);
++    }
++
++    *sipCppPtr = ql;
++
++    return sipGetState(sipTransferObj);
++%End
++};
++%End
++
++// QList<double> is implemented as a Python list of doubles.
++%MappedType QList<double>
++{
++%TypeHeaderCode
++#include <qlist.h>
++%End
++
++%ConvertFromTypeCode
++    // Create the list.
++    PyObject *l;
++
++    if ((l = PyList_New(sipCpp->size())) == NULL)
++        return NULL;
++
++    // Set the list elements.
++    for (int i = 0; i < sipCpp->size(); ++i)
++    {
++        PyObject *pobj;
++
++        if ((pobj = PyFloat_FromDouble(sipCpp->value(i))) == NULL)
++        {
++            Py_DECREF(l);
++
++            return NULL;
++        }
++
++        PyList_SET_ITEM(l, i, pobj);
++    }
++
++    return l;
++%End
++
++%ConvertToTypeCode
++    // Check the type if that is all that is required.
++    if (sipIsErr == NULL)
++        return (PySequence_Check(sipPy) && PySequence_Size(sipPy) >= 0);
++
++    QList<double> *ql = new QList<double>;
++    SIP_SSIZE_T len = PySequence_Size(sipPy);
++
++    for (SIP_SSIZE_T i = 0; i < len; ++i)
++        ql->append(PyFloat_AsDouble(PySequence_ITEM(sipPy, i)));
++
++    *sipCppPtr = ql;
++
++    return sipGetState(sipTransferObj);
++%End
++};
++
++%End
diff --git a/buildconfig/dev-packages/rpm/mantid-developer/mantid-developer.spec b/buildconfig/dev-packages/rpm/mantid-developer/mantid-developer.spec
index 1dea7f388369cdfc1711a3fa6d85137fa25b273b..611aa020b90a1b9222b6a30682a4da72d0d7c5a6 100644
--- a/buildconfig/dev-packages/rpm/mantid-developer/mantid-developer.spec
+++ b/buildconfig/dev-packages/rpm/mantid-developer/mantid-developer.spec
@@ -1,11 +1,11 @@
-%if 0%{?fedora} || 0%{?rhel} >= 8
+%if 0%{?fedora} || 0%{?rhel} >= 7
   %global with_python3 1
 %else
   %global with_python3 0
 %endif
 
 Name:           mantid-developer
-Version:        1.35
+Version:        1.37
 Release:        1%{?dist}
 Summary:        Meta Package to install dependencies for Mantid Development
 
@@ -16,20 +16,23 @@ BuildRoot:      %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n)
 
 %{?fedora:Requires: rpmfusion-nonfree-release}
 %{?rhel:Requires: epel-release}
-Requires: clang
 %{?fedora:Requires: cmake-gui}
 %{?rhel:Requires: cmake3-gui}
+%{?fedora:Requires: python2-qtconsole}
 Requires: boost169-devel
 Requires: boost169-python2-devel
+Requires: clang
 Requires: doxygen
-Requires: gperftools-devel
-Requires: gperftools-libs
+Requires: dvipng
 Requires: gcc-c++
+Requires: git
 Requires: git-all
+Requires: gperftools-devel
+Requires: gperftools-libs
+Requires: graphviz
 Requires: gsl-devel
-Requires: hdf-devel
 Requires: hdf5-devel
-Requires: h5py >= 2.3.1
+Requires: hdf-devel
 Requires: jsoncpp-devel >= 0.7.0
 Requires: librdkafka-devel
 Requires: muParser-devel
@@ -39,27 +42,40 @@ Requires: nexus-devel >= 4.2
 Requires: ninja-build
 Requires: numpy
 Requires: OCE-devel
+Requires: openssl-devel
 Requires: poco-devel >= 1.4.6
 Requires: PyQt4-devel
-Requires: python2-qt5-devel
-Requires: python-QtPy
-Requires: python-requests
-Requires: python-devel
-Requires: python-setuptools
-Requires: python-ipython >= 1.1
+Requires: python2-h5py >= 2.3.1
 Requires: python2-matplotlib
 Requires: python2-matplotlib-qt4
 Requires: python2-matplotlib-qt4
-Requires: python-pip
-%{?fedora:Requires: python2-qtconsole}
-Requires: python-sphinx
-Requires: python2-sphinx-bootstrap-theme
-Requires: PyYAML
 Requires: python2-mock
 Requires: python2-psutil
+Requires: python2-qt5-devel
+%{?fedora:Requires: python2-qtconsole}
+Requires: python2-sphinx-bootstrap-theme
+Requires: python-devel
 Requires: python-enum34
+Requires: python-ipython >= 1.1
+Requires: python-pip
+Requires: python-QtPy
+Requires: python-requests
+Requires: python-setuptools
+Requires: python-sphinx
+Requires: PyYAML
 Requires: qscintilla-devel
+Requires: qscintilla-qt5-devel
+Requires: qt5-qtbase-devel
+Requires: qt5-qtbase-gui
+Requires: qt5-qtimageformats
+Requires: qt5-qtsvg
+Requires: qt5-qttools-devel
+Requires: qt5-qttools-libs-designer
+Requires: qt5-qtwebkit-devel
+Requires: qt5-qtx11extras
+Requires: qt5-qtx11extras-devel
 Requires: qt-devel >= 4.6
+Requires: qtwebkit-devel
 Requires: qwt5-qt4-devel
 Requires: qwtplot3d-qt4-devel
 Requires: redhat-lsb
@@ -68,61 +84,32 @@ Requires: scipy
 Requires: sip-devel
 Requires: tbb
 Requires: tbb-devel
-Requires: git
-Requires: openssl-devel
 Requires: texlive-latex
 Requires: texlive-latex-bin
 Requires: texlive-was
 Requires: tex-preview
-Requires: dvipng
-Requires: qt-devel
-Requires: qtwebkit-devel
-Requires: qt5-qtbase-devel
-Requires: qt5-qtbase-gui
-Requires: qt5-qtimageformats
-Requires: qt5-qtsvg
-Requires: qt5-qttools-devel
-Requires: qt5-qttools-libs-designer
-Requires: qt5-qtwebkit-devel
-Requires: qt5-qtx11extras
-Requires: qt5-qtx11extras-devel
-Requires: qscintilla-qt5-devel
-Requires: graphviz
-%if %{with_python3}
-Requires: python3-setuptools
-Requires: python3-sip-devel
-Requires: python3-PyQt4-devel
-Requires: python3-qt5-devel
-Requires: python3-QtPy
-Requires: python3-numpy
-Requires: python3-scipy
-Requires: python3-scikit-image
-Requires: python3-sphinx
-Requires: python3-sphinx-bootstrap-theme
-Requires: python3-dateutil
-Requires: python3-h5py
-Requires: python3-ipython-gui
-Requires: python3-matplotlib
-%{?fedora:Requires: python3-qtconsole}
-Requires: python3-PyYAML
-Requires: python3-mock
-%{?fedora:Requires: python3-psutil}
-%{?fedora:Requires: python3-requests}
-Requires: boost-python3-devel
-%endif
+Requires: zeromq
 
-%if 0%{?el7}
-Requires: python36-setuptools
-Requires: python36-qt5-devel
-Requires: python36-numpy
-Requires: python36-scipy
-Requires: python36-sphinx
-Requires: python36-dateutil
-Requires: python36-PyYAML
-Requires: python36-mock
-Requires: python36-psutil
-Requires: python36-requests
+%if %{with_python3}
 Requires: boost169-python3-devel
+Requires: python%{python3_pkgversion}-dateutil
+Requires: python%{python3_pkgversion}-h5py
+Requires: python%{python3_pkgversion}-ipython
+Requires: python%{python3_pkgversion}-ipython-gui
+Requires: python%{python3_pkgversion}-matplotlib-qt5
+Requires: python%{python3_pkgversion}-numpy
+Requires: python%{python3_pkgversion}-psutil
+%{?fedora:Requires: python%{python3_pkgversion}-PyQt4-devel}
+Requires: python%{python3_pkgversion}-PyYAML
+Requires: python%{python3_pkgversion}-qt5-devel
+%{?fedora:Requires: python%{python3_pkgversion}-qtconsole}
+Requires: python%{python3_pkgversion}-QtPy
+Requires: python%{python3_pkgversion}-requests
+Requires: python%{python3_pkgversion}-scipy
+Requires: python%{python3_pkgversion}-setuptools
+Requires: python%{python3_pkgversion}-sphinx
+Requires: python%{python3_pkgversion}-sphinx-bootstrap-theme
+Requires: python%{python3_pkgversion}-PyYAML
 %endif
 
 BuildArch: noarch
@@ -146,6 +133,11 @@ required for Mantid development.
 %files
 
 %changelog
+* Thu Jan 16 2020 Martyn Gigg <martyn.gigg@stfc.ac.uk>
+- Merge fedora and rhel python 3 packages using python3_pkgversion
+
+* Tue Jan 07 2020 Martyn Gigg <martyn.gigg@stfc.ac.uk>
+- Add remaining python36 packages required for mantid.
 
 * Tue Dec 03 2019 David Fairbrother <david.fairbrother@stfc.ac.uk>
 - Added Python Enum34 back-port to the required Python dependencies
diff --git a/qt/applications/workbench/workbench/plotting/figureerrorsmanager.py b/qt/applications/workbench/workbench/plotting/figureerrorsmanager.py
index f77488c193feb767a6c6a1aa1747c750a001b2d8..ac8d16650e1b1d1cb826c9601b41b6c73f3e8b80 100644
--- a/qt/applications/workbench/workbench/plotting/figureerrorsmanager.py
+++ b/qt/applications/workbench/workbench/plotting/figureerrorsmanager.py
@@ -12,8 +12,8 @@ from matplotlib.container import ErrorbarContainer
 from matplotlib.lines import Line2D
 from mantid.plots import MantidAxes
 from mantid.plots.helperfunctions import get_data_from_errorbar_container, set_errorbars_hidden
+from mantid.plots.legend import LegendProperties
 from mantidqt.widgets.plotconfigdialog.curvestabwidget import curve_has_errors, CurveProperties, remove_curve_from_ax
-from mantidqt.widgets.plotconfigdialog.legendtabwidget import LegendProperties
 
 
 class FigureErrorsManager(object):
diff --git a/qt/python/mantidqt/CMakeLists.txt b/qt/python/mantidqt/CMakeLists.txt
index 9b9f0c6aa1d0b80c3285a06b3c8ca743695b9e6e..9d3c39a7bfb43073dda0e5d9aa51e831634fbcc8 100644
--- a/qt/python/mantidqt/CMakeLists.txt
+++ b/qt/python/mantidqt/CMakeLists.txt
@@ -38,9 +38,6 @@ if(ENABLE_MANTIDPLOT)
                        ${CMAKE_CURRENT_LIST_DIR}
                        ${COMMON_SIP_DIR}
                        ${CMAKE_SOURCE_DIR}/Framework/PythonInterface/core/inc
-                     SYSTEM_INCLUDE_DIRS
-                       ${PYTHON_INCLUDE_PATH}
-                       ${SIP_INCLUDE_DIR}
                      LINK_LIBS
                        ${common_link_libs}
                        MantidQtWidgetsCommonQt4
@@ -72,9 +69,6 @@ if(ENABLE_WORKBENCH)
                        ${CMAKE_CURRENT_LIST_DIR}
                        ${COMMON_SIP_DIR}
                        ${CMAKE_SOURCE_DIR}/Framework/PythonInterface/core/inc
-		     SYSTEM_INCLUDE_DIRS
-                       ${PYTHON_INCLUDE_PATH}
-                       ${SIP_INCLUDE_DIR}
                      LINK_LIBS
                        MantidQtWidgetsCommonQt5
                        ${common_link_libs}
diff --git a/qt/python/mantidqt/icons/CMakeLists.txt b/qt/python/mantidqt/icons/CMakeLists.txt
index c4f3820ee5865c74bb63c93ce1975a7167c8c193..b669302bedc108ad9dc66a6c32385fcebbf5628f 100644
--- a/qt/python/mantidqt/icons/CMakeLists.txt
+++ b/qt/python/mantidqt/icons/CMakeLists.txt
@@ -15,9 +15,6 @@ if(ENABLE_MANTIDPLOT)
                       ${COMMON_SIP_DIR}
                       ${CMAKE_SOURCE_DIR}/Framework/PythonInterface/core/inc
                       ${ICONS_INC_DIR}
-		     SYSTEM_INCLUDE_DIRS
-                      ${PYTHON_INCLUDE_PATH}
-                      ${SIP_INCLUDE_DIR}
                      LINK_LIBS
                       MantidQtIconsQt4
                       Qt4::QtCore
@@ -44,9 +41,6 @@ if(ENABLE_WORKBENCH)
                       ${COMMON_SIP_DIR}
                       ${ICONS_INC_DIR}
                       ${CMAKE_SOURCE_DIR}/Framework/PythonInterface/core/inc
-		     SYSTEM_INCLUDE_DIRS
-                      ${PYTHON_INCLUDE_PATH}
-                      ${SIP_INCLUDE_DIR}
                      LINK_LIBS
                       MantidQtIconsQt5
                       Qt5::Core
diff --git a/qt/python/mantidqt/project/plotsloader.py b/qt/python/mantidqt/project/plotsloader.py
index 309998423ad26e10d528cf38546021858f0e1b0e..30505f16ee118ad6e51fc158c92b527df0f2765e 100644
--- a/qt/python/mantidqt/project/plotsloader.py
+++ b/qt/python/mantidqt/project/plotsloader.py
@@ -16,9 +16,10 @@ from matplotlib import axis, ticker  # noqa
 
 from mantid import logger
 from mantid.api import AnalysisDataService as ADS
+from mantid.plots.legend import LegendProperties
 # Constants set in workbench.plotting.functions but would cause backwards reliability
 from mantidqt.plotting.functions import pcolormesh
-from mantidqt.widgets.plotconfigdialog.legendtabwidget import LegendProperties
+
 
 SUBPLOT_WSPACE = 0.5
 SUBPLOT_HSPACE = 0.5
diff --git a/qt/python/mantidqt/project/plotssaver.py b/qt/python/mantidqt/project/plotssaver.py
index 615fd2dab26a51ab5dddd8f0e123f66aad1f1fcd..2622abeac9ea197d8b944400e44533dcd4b73cd4 100644
--- a/qt/python/mantidqt/project/plotssaver.py
+++ b/qt/python/mantidqt/project/plotssaver.py
@@ -13,7 +13,7 @@ from matplotlib import ticker
 from matplotlib.image import AxesImage
 
 from mantid import logger
-from mantidqt.widgets.plotconfigdialog.legendtabwidget import LegendProperties
+from mantid.plots.legend import LegendProperties
 
 try:
     from matplotlib.colors import to_hex
diff --git a/qt/python/mantidqt/project/test/test_plotssaver.py b/qt/python/mantidqt/project/test/test_plotssaver.py
index c2d6dc985d86846a5b5755fd06d0b9d42b3e4476..5c6672e6ee0894ead41ab068e5b187425c25ac4b 100644
--- a/qt/python/mantidqt/project/test/test_plotssaver.py
+++ b/qt/python/mantidqt/project/test/test_plotssaver.py
@@ -9,7 +9,6 @@
 from __future__ import (absolute_import, division, print_function, unicode_literals)
 
 import matplotlib
-import platform
 import unittest
 
 from mantid.api import AnalysisDataService as ADS
@@ -33,11 +32,11 @@ class PlotsSaverTest(unittest.TestCase):
                                                         u'background_color': u'#ffffff',
                                                         u'edge_color': u'#000000',
                                                         u'transparency': 0.5,
-                                                        u'entries_font': u'Bitstream Vera Sans',
-                                                        u'entries_size': 10,
+                                                        u'entries_font': u'DejaVu Sans',
+                                                        u'entries_size': 10.0,
                                                         u'entries_color': u'#000000',
-                                                        u'title_font': u'Bitstream Vera Sans',
-                                                        u'title_size': 12,
+                                                        u'title_font': u'DejaVu Sans',
+                                                        u'title_size': 12.0,
                                                         u'title_color': u'#000000',
                                                         u'marker_size': 2.0,
                                                         u'box_visible': True,
@@ -128,11 +127,6 @@ class PlotsSaverTest(unittest.TestCase):
 
         self.loader_plot_dict[u'creationArguments'] = [[{u"specNum": 2, "function": "plot"}]]
 
-        # The original font isn't available on Windows so it has to be changed.
-        if platform.system() == "Windows":
-            self.loader_plot_dict[u'axes'][0][u'legend'][u'entries_font'] = 'DejaVu Sans'
-            self.loader_plot_dict[u'axes'][0][u'legend'][u'title_font'] = 'DejaVu Sans'
-
         self.maxDiff = None
         self.assertDictEqual(return_value, self.loader_plot_dict)
 
@@ -142,11 +136,6 @@ class PlotsSaverTest(unittest.TestCase):
 
         expected_value = self.loader_plot_dict["axes"][0]
 
-        # The original font isn't available on Windows so it has to be changed.
-        if platform.system() == "Windows":
-            self.loader_plot_dict[u'axes'][0][u'legend'][u'entries_font'] = 'DejaVu Sans'
-            self.loader_plot_dict[u'axes'][0][u'legend'][u'title_font'] = 'DejaVu Sans'
-
         self.maxDiff = None
         self.assertDictEqual(return_value, expected_value)
 
diff --git a/qt/python/mantidqt/widgets/codeeditor/completion.py b/qt/python/mantidqt/widgets/codeeditor/completion.py
index d5290d19f1ff06ca625b9dd5248965869e04d27c..6019a7394bcc801ffbd558f18a2935e2553c4fcd 100644
--- a/qt/python/mantidqt/widgets/codeeditor/completion.py
+++ b/qt/python/mantidqt/widgets/codeeditor/completion.py
@@ -71,7 +71,7 @@ def get_builtin_argspec(builtin):
     if not doc_string:
         return None
     func_descriptor = doc_string.split('\n')[0].strip()
-    if re.search(builtin.__name__ + "\([\[\*a-zA-Z_].*\)", func_descriptor):
+    if re.search(builtin.__name__ + r"\([\[\*a-zA-Z_].*\)", func_descriptor):
         args_string = func_descriptor[func_descriptor.find('(') + 1:func_descriptor.rfind(')')]
         all_args_list = args_string.split(', ')
         args = []
diff --git a/qt/python/mantidqt/widgets/instrumentview/CMakeLists.txt b/qt/python/mantidqt/widgets/instrumentview/CMakeLists.txt
index a2603e5a518875bfe3bba7570ededfb8d270a870..4b171ed2a4f77167de990a7d87b54df26d08f4f1 100644
--- a/qt/python/mantidqt/widgets/instrumentview/CMakeLists.txt
+++ b/qt/python/mantidqt/widgets/instrumentview/CMakeLists.txt
@@ -10,9 +10,6 @@ mtd_add_sip_module(MODULE_NAME _instrumentviewqt5
                    TARGET_NAME mantidqt_instrumentviewqt5
                    SIP_SRCS _instrumentview.sip
                    PYQT_VERSION 5
-                   SYSTEM_INCLUDE_DIRS
-                     ${PYTHON_INCLUDE_PATH}
-                     ${SIP_INCLUDE_DIR}
                    LINK_LIBS
                      ${common_link_libs}
                      MantidQtWidgetsInstrumentViewQt5
diff --git a/qt/python/mantidqt/widgets/plotconfigdialog/colorselector.py b/qt/python/mantidqt/widgets/plotconfigdialog/colorselector.py
index 28cd82e7ed12864867d3ea55e8b11ca229c2772c..5e72ce5576633d10b0cca9f18b1f70f45cdf2b8a 100644
--- a/qt/python/mantidqt/widgets/plotconfigdialog/colorselector.py
+++ b/qt/python/mantidqt/widgets/plotconfigdialog/colorselector.py
@@ -8,22 +8,14 @@
 
 from __future__ import (absolute_import, unicode_literals)
 
-from matplotlib import colors, rcParams
+from mantid.plots.legend import convert_color_to_hex
+from matplotlib import rcParams
 from qtpy.QtCore import QRegExp
 from qtpy.QtGui import QColor, QPalette, QRegExpValidator
 from qtpy.QtWidgets import (QWidget, QLineEdit, QPushButton, QHBoxLayout,
                             QColorDialog)
 
 
-def convert_color_to_hex(color):
-    """Convert a matplotlib color to its hex form"""
-    try:
-        return colors.cnames[color]
-    except (KeyError, TypeError):
-        rgb = colors.colorConverter.to_rgb(color)
-        return colors.rgb2hex(rgb)
-
-
 MPL_DEFAULT = convert_color_to_hex(rcParams['lines.color'])
 
 
diff --git a/qt/python/mantidqt/widgets/plotconfigdialog/curvestabwidget/presenter.py b/qt/python/mantidqt/widgets/plotconfigdialog/curvestabwidget/presenter.py
index a634b85b517b1ddb427f862dd76a11f5f081a908..613c8c8d1c582e3d5dc0402344b722e08e604695 100644
--- a/qt/python/mantidqt/widgets/plotconfigdialog/curvestabwidget/presenter.py
+++ b/qt/python/mantidqt/widgets/plotconfigdialog/curvestabwidget/presenter.py
@@ -8,12 +8,12 @@
 
 from __future__ import (absolute_import, unicode_literals)
 
+from mantid.plots.legend import LegendProperties
 from mantidqt.utils.qt import block_signals
 from mantidqt.widgets.plotconfigdialog import get_axes_names_dict, curve_in_ax
 from mantidqt.widgets.plotconfigdialog.curvestabwidget import (
     CurveProperties, curve_has_errors, remove_curve_from_ax)
 from mantidqt.widgets.plotconfigdialog.curvestabwidget.view import CurvesTabWidgetView
-from mantidqt.widgets.plotconfigdialog.legendtabwidget import LegendProperties
 from workbench.plotting.figureerrorsmanager import FigureErrorsManager
 
 
diff --git a/qt/python/mantidqt/widgets/plotconfigdialog/legendtabwidget/__init__.py b/qt/python/mantidqt/widgets/plotconfigdialog/legendtabwidget/__init__.py
index 5e5ffb2aea1f1e66d05ccd6c287e3fe352bb7dba..db65bfdd56145fdd2d6d2eccf8d7684e66930c50 100644
--- a/qt/python/mantidqt/widgets/plotconfigdialog/legendtabwidget/__init__.py
+++ b/qt/python/mantidqt/widgets/plotconfigdialog/legendtabwidget/__init__.py
@@ -7,155 +7,3 @@
 #  This file is part of the mantid workbench.
 
 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
-
-import sys
-
-
-class LegendProperties(dict):
-    def __getattr__(self, item):
-        return self[item]
-
-    @classmethod
-    def from_legend(cls, legend):
-        props = dict()
-
-        props['visible'] = legend.get_visible()
-
-        title = legend.get_title()
-        if sys.version_info[0] >= 3:
-            if isinstance(title.get_text(), str):
-                props['title'] = title.get_text()
-            else:
-                props['title'] = None
-        else:
-            if isinstance(title.get_text(), unicode):
-                props['title'] = title.get_text()
-            else:
-                props['title'] = None
-
-        props['title_font'] = title.get_fontname()
-        props['title_size'] = title.get_fontsize()
-        props['title_color'] = convert_color_to_hex(title.get_color())
-
-        props['box_visible'] = legend.get_frame_on()
-
-        box = legend.get_frame()
-        props['background_color'] = convert_color_to_hex(box.get_facecolor())
-        props['edge_color'] = convert_color_to_hex(box.get_edgecolor())
-        props['transparency'] = box.get_alpha()
-
-        text = legend.get_texts()[0]
-        props['entries_font'] = text.get_fontname()
-        props['entries_size'] = text.get_fontsize()
-        props['entries_color'] = convert_color_to_hex(text.get_color())
-
-        props['marker_size'] = legend.handlelength
-        props['shadow'] = legend.shadow
-
-        boxstyle = legend.legendPatch.get_boxstyle()
-        if isinstance(boxstyle, BoxStyle.Round):
-            props['round_edges'] = True
-        else:
-            props['round_edges'] = False
-
-        props['columns'] = legend._ncol
-        props['column_spacing'] = legend.columnspacing
-        props['label_spacing'] = legend.labelspacing
-
-        position = legend._legend_handle_box.get_children()[0].align
-        if position == "baseline":
-            props['marker_position'] = "Left of Entries"
-        else:
-            props['marker_position'] = "Right of Entries"
-
-        props['markers'] = legend.numpoints
-        props['border_padding'] = legend.borderpad
-        props['marker_label_padding'] = legend.handletextpad
-
-        return cls(props)
-
-    @classmethod
-    def from_view(cls, view):
-        props = dict()
-        props['visible'] = not view.hide_legend_check_box.isChecked()
-        props['title'] = view.get_title()
-        props['background_color'] = view.get_background_color()
-        props['edge_color'] = view.get_edge_color()
-        props['transparency'] = (100-float(view.get_transparency_spin_box_value()))/100
-        props['entries_font'] = view.get_entries_font()
-        props['entries_size'] = view.get_entries_size()
-        props['entries_color'] = view.get_entries_color()
-        props['title_font'] = view.get_title_font()
-        props['title_size'] = view.get_title_size()
-        props['title_color'] = view.get_title_color()
-        props['marker_size'] = view.get_marker_size()
-        props['box_visible'] = not view.get_hide_box()
-        return cls(props)
-
-    @classmethod
-    def from_view_advanced(cls, view):
-        props = dict()
-        props['shadow'] = view.get_shadow()
-        props['round_edges'] = view.get_round_edges()
-        props['columns'] = view.get_number_of_columns()
-        props['column_spacing'] = view.get_column_spacing()
-        props['label_spacing'] = view.get_label_spacing()
-        props['marker_position'] = view.get_marker_position()
-        props['markers'] = view.get_number_of_markers()
-        props['border_padding'] = view.get_border_padding()
-        props['marker_label_padding'] = view.get_marker_label_padding()
-        return cls(props)
-
-    @classmethod
-    def create_legend(cls, props, ax):
-        if int(matplotlib.__version__[0]) >= 2:
-            legend = ax.legend(ncol=props['columns'],
-                               prop={'size': props['entries_size']},
-                               numpoints=props['markers'],
-                               markerfirst=props['marker_position'] == "Left of Entries",
-                               frameon=props['box_visible'],
-                               fancybox=props['round_edges'],
-                               shadow=props['shadow'],
-                               framealpha=props['transparency'],
-                               facecolor=props['background_color'],
-                               edgecolor=props['edge_color'],
-                               title=props['title'],
-                               borderpad=props['border_padding'],
-                               labelspacing=props['label_spacing'],
-                               handlelength=props['marker_size'],
-                               handletextpad=props['marker_label_padding'],
-                               columnspacing=props['column_spacing'])
-        else:
-            legend = ax.legend(ncol=props['columns'],
-                               prop={'size': props['entries_size']},
-                               numpoints=props['markers'],
-                               markerfirst=props['marker_position'] == "Left of Entries",
-                               frameon=props['box_visible'],
-                               fancybox=props['round_edges'],
-                               shadow=props['shadow'],
-                               framealpha=props['transparency'],
-                               title=props['title'],
-                               borderpad=props['border_padding'],
-                               labelspacing=props['label_spacing'],
-                               handlelength=props['marker_size'],
-                               handletextpad=props['marker_label_padding'],
-                               columnspacing=props['column_spacing'])
-
-        title = legend.get_title()
-        title.set_fontname(props['title_font'])
-        title.set_fontsize(props['title_size'])
-        title.set_color(props['title_color'])
-
-        for text in legend.get_texts():
-            text.set_fontname(props['entries_font'])
-            text.set_fontsize(props['entries_size'])
-            text.set_color(props['entries_color'])
-
-        legend.set_visible(props['visible'])
-
-        legend_set_draggable(legend, True)
diff --git a/qt/python/mantidqt/widgets/plotconfigdialog/legendtabwidget/advancedlegendoptionsdialog/view.py b/qt/python/mantidqt/widgets/plotconfigdialog/legendtabwidget/advancedlegendoptionsdialog/view.py
index f8d52ef6f10b9e257f3b67b4582658727505e068..aeb8953684ace67c7cd50df244fcf0398b704450 100644
--- a/qt/python/mantidqt/widgets/plotconfigdialog/legendtabwidget/advancedlegendoptionsdialog/view.py
+++ b/qt/python/mantidqt/widgets/plotconfigdialog/legendtabwidget/advancedlegendoptionsdialog/view.py
@@ -12,8 +12,8 @@ from qtpy.QtCore import Qt, QSize
 from qtpy.QtGui import QIcon
 from qtpy.QtWidgets import QDialog
 
+from mantid.plots.legend import LegendProperties
 from mantidqt.utils.qt import load_ui
-from mantidqt.widgets.plotconfigdialog.legendtabwidget import LegendProperties
 
 
 class AdvancedLegendOptionsView(QDialog):
diff --git a/qt/python/mantidqt/widgets/plotconfigdialog/legendtabwidget/presenter.py b/qt/python/mantidqt/widgets/plotconfigdialog/legendtabwidget/presenter.py
index d93aac576de27500dffe04ff5d8d227dafe7c4ee..7e46f40b31d7dbaca54981b25802b0e151dfec3d 100644
--- a/qt/python/mantidqt/widgets/plotconfigdialog/legendtabwidget/presenter.py
+++ b/qt/python/mantidqt/widgets/plotconfigdialog/legendtabwidget/presenter.py
@@ -8,8 +8,8 @@
 
 from __future__ import (absolute_import, unicode_literals)
 
+from mantid.plots.legend import LegendProperties
 from mantidqt.widgets.plotconfigdialog.legendtabwidget.view import LegendTabWidgetView
-from mantidqt.widgets.plotconfigdialog.legendtabwidget import LegendProperties
 
 import matplotlib
 import matplotlib.font_manager
diff --git a/qt/python/mantidqt/widgets/plotconfigdialog/legendtabwidget/view.py b/qt/python/mantidqt/widgets/plotconfigdialog/legendtabwidget/view.py
index 44e930baad4cf1f5700e100d3abdcde0d3b250cf..03688639f8b6562fc1a4f83d731b438ac8a818da 100644
--- a/qt/python/mantidqt/widgets/plotconfigdialog/legendtabwidget/view.py
+++ b/qt/python/mantidqt/widgets/plotconfigdialog/legendtabwidget/view.py
@@ -11,9 +11,9 @@ from __future__ import (absolute_import, unicode_literals)
 from qtpy.QtCore import Qt
 from qtpy.QtWidgets import QWidget
 
+from mantid.plots.legend import LegendProperties
 from mantidqt.utils.qt import load_ui
 from mantidqt.widgets.plotconfigdialog.colorselector import ColorSelector
-from mantidqt.widgets.plotconfigdialog.legendtabwidget import LegendProperties
 from mantidqt.widgets.plotconfigdialog.legendtabwidget.advancedlegendoptionsdialog.view import AdvancedLegendOptionsView
 
 
diff --git a/qt/python/mantidqt/widgets/plotconfigdialog/test/test_apply_all_properties.py b/qt/python/mantidqt/widgets/plotconfigdialog/test/test_apply_all_properties.py
index 142baa29fbdb5d94be52dbe0d96270873ac130f7..a018b5bb42f9a4a521dc69324804a95daad59ead 100644
--- a/qt/python/mantidqt/widgets/plotconfigdialog/test/test_apply_all_properties.py
+++ b/qt/python/mantidqt/widgets/plotconfigdialog/test/test_apply_all_properties.py
@@ -16,12 +16,12 @@ from matplotlib.colors import LogNorm
 from matplotlib.patches import BoxStyle
 from matplotlib.pyplot import figure
 
+from mantid.plots.legend import LegendProperties
 from mantid.py3compat.mock import Mock, patch
 from mantidqt.widgets.plotconfigdialog.colorselector import convert_color_to_hex
 from mantidqt.widgets.plotconfigdialog.axestabwidget import AxProperties
 from mantidqt.widgets.plotconfigdialog.imagestabwidget import ImageProperties
 from mantidqt.widgets.plotconfigdialog.curvestabwidget import CurveProperties
-from mantidqt.widgets.plotconfigdialog.legendtabwidget import LegendProperties
 from mantidqt.widgets.plotconfigdialog.presenter import PlotConfigDialogPresenter
 
 AX_VIEW = 'mantidqt.widgets.plotconfigdialog.axestabwidget.presenter.AxesTabWidgetView'
diff --git a/qt/python/mantidqtpython/CMakeLists.txt b/qt/python/mantidqtpython/CMakeLists.txt
index 12e643447252a9f92fe42edf8012e8601803e112..ed8af025ecd28e357f95a292cbb99c8732a78882 100644
--- a/qt/python/mantidqtpython/CMakeLists.txt
+++ b/qt/python/mantidqtpython/CMakeLists.txt
@@ -89,9 +89,6 @@ mtd_add_sip_module(MODULE_NAME mantidqtpython
                      ${CMAKE_CURRENT_LIST_DIR}
                      ${COMMON_SIP_DIR}
                      ../../../Framework/PythonInterface/core/inc
-                   SYSTEM_INCLUDE_DIRS
-                     ${PYTHON_INCLUDE_PATH}
-                     ${SIP_INCLUDE_DIR}
                    PYQT_VERSION 4
                    LINK_LIBS
                      ${TCMALLOC_LIBRARIES_LINKTIME}
diff --git a/scripts/HFIR_4Circle_Reduction/process_mask.py b/scripts/HFIR_4Circle_Reduction/process_mask.py
index 39be7b70ce638cda69c055ea43eb3f7d0d00e092..700e82e2db96fa4d3f6ad7e0df2437e87c8b3974 100644
--- a/scripts/HFIR_4Circle_Reduction/process_mask.py
+++ b/scripts/HFIR_4Circle_Reduction/process_mask.py
@@ -4,6 +4,8 @@
 #     NScD Oak Ridge National Laboratory, European Spallation Source
 #     & Institut Laue - Langevin
 # SPDX - License - Identifier: GPL - 3.0 +
+from __future__ import print_function
+
 import math
 import numpy
 import re
@@ -35,7 +37,7 @@ def parse_mask(xml_name):
     det_range_list = re.split(',', det_list_str)
 
     for det_range in det_range_list:
-        print det_range
+        print(det_range)
 
     # int_count = 0