#For PyQt5's direct stance on compatibility with PyQt4, see this link:
#  - http://pyqt.sourceforge.net/Docs/PyQt5/pyqt4_differences.html
#
#Patching notes:
# - All QMatrix usages may need to be converted to QTransform.  QMatrix is now
#    obsolete.  Fortunately, Qt has preserved the conversion of them by using
#    the QTransform(QMatrix) overload.  Many functions simply do not support
#    QMatrix anymore and there is no replacement that is not a QTransform.  eg:
#    see QPainter.combinedMatrix() and QPainter.deviceMatrix()... their new
#    replacements *only* deal with QTransform.

import sys
import inspect
from itertools import izip
import warnings
import pkgutil


try:
    import PyQt5
except ImportError:
    raise ImportError("qt_backport emulation currently requires PyQt5")
from PyQt5 import QtWidgets      as _PyQt5_QtWidgets
from PyQt5 import QtGui          as _PyQt5_QtGui
from PyQt5 import QtCore         as _PyQt5_QtCore
from PyQt5 import QtPrintSupport as _PyQt5_QtPrintSupport
from PyQt5 import QtMultimedia   as _PyQt5_QtMultimedia
from PyQt5 import QtX11Extras    as _PyQt5_QtX11Extras

import qt_backport

_OBSOLETE_ = "__obsolete__in__Qt5__"

def get_qt_class_names(module):
    return tuple(sorted(x.__name__ for x in module.__dict__.values() 
                        if (inspect.isclass(x) and x.__name__.startswith("Q"))))

#Set the list of classes that need to be re-assembled back into QtGui...
# - note that using get_qt_class_names to find backports will cause Qt5-only
#   classes to appear in the PyQt4.QtGui namespace.  Since our only purpose is
#   to make old code work, this namespace pollution is fine.  Old code will not
#   use them.  We will try and be tidy when it would be excessive, though (eg:
#   only a few QtGui classes ended up moving to QtCore).
QtWidgets_backports = get_qt_class_names(_PyQt5_QtWidgets) #new module
QtPrintSupport_backports = get_qt_class_names(_PyQt5_QtPrintSupport) #new module
QtCore_backports = ('QAbstractProxyModel', 'QItemSelection', 'QItemSelectionModel', 'QItemSelectionRange', 'QSortFilterProxyModel', 'QStringListModel', )
QtMultimedia_backports = ('QSound', )
QtX11Extras_backports = ('QX11Info', )

#class names below were dropped from Qt4 to Qt5
# - this is not used, but is for reference if/when resolving issues
# - list was generated by comparing PySide.QtGui to PyQt5.QtGui, so this may
#    not be 100% correct.  The correct statement is "PySide.QtGui had these
#    classes, but they do not exist in Qt5"
unavailable_in_qt5 = ('AbstractPageSetupDialog', 'QCDEStyle', 'QCleanlooksStyle', 'QClipboardEvent', 'QGraphicsItemAnimation', 'QGtkStyle', 'QIconEngine', 'QInputContext', 'QInputContextFactory', 'QMotifStyle', 'QPlastiqueStyle', 'QProxyModel', 'QPyTextObject', 'QToolBarChangeEvent', 'QWindowsStyle', 'QWorkspace', 'QX11EmbedContainer', 'QX11EmbedWidget')

#class names that exist in Qt5, but are not available in PyQt5 for some reason
# - also not used, but here for reference
unavailable_in_pyqt5 = (
    'QAccessibleEvent', #http://qt-project.org/doc/qt-5/qaccessibleevent.html
    'QMatrix', #deprecated (but 2x2 etc variants still exist for some reason)
    'QTileRules', #http://qt-project.org/doc/qt-5/qtilerules.html
    # style option synonyms below are not in PyQt5, but are doc'd in Qt5 ###
    'QStyleOptionDockWidgetV2','QStyleOptionFrameV2','QStyleOptionFrameV3', 'QStyleOptionProgressBarV2','QStyleOptionTabBarBaseV2', 'QStyleOptionTabV2', 'QStyleOptionTabV3', 'QStyleOptionToolBoxV2','QStyleOptionViewItemV2', 'QStyleOptionViewItemV3','QStyleOptionViewItemV4',
)

def _get_importable_modules(src_pkg):
    module_names = \
        [importer[1] for importer in pkgutil.iter_modules(src_pkg.__path__) \
         if not importer[1].startswith("_")]
    return sorted(module_names)

def load_modules(dst_pkg):
    #Make a full copy of the PyQt5 package...
    # - Note that we are dynamically adding a copy of the PyQt5 modules, where
    #    each of the contained classes are copies of the original classes so that
    #    we can patch them to work like PyQt4/PySide without altering the PyQt5
    #    interface itself.  This enables new PyQt5 applications to work, while
    #    enabling old(er) packages built with PyQt4/PySide to work with their
    #    expected API.
    _uncopyable_classes = (
        "PyQt5.QtCore.pyqtSignal", #not subclassable
        "PyQt5.QtCore.pyqtBoundSignal", #not subclassable
        "PyQt5.QtCore.QtMsgType",  #segfaults when you try and subclass it
        #Qt module duplicates (almost?) everything... hmm.
        "PyQt5.Qt.pyqtSignal",
        "PyQt5.Qt.pyqtBoundSignal", #not subclassable
        "PyQt5.Qt.QtMsgType",
    )
    _all_PyQt5_modules = _get_importable_modules(PyQt5)
    for _module_name in _all_PyQt5_modules:
        _qt5_module_name = "PyQt5." + _module_name
        _dst_module_name = dst_pkg.__name__ + "." + _module_name
        try:
            _dst_emulator = _copy_qt_module(_qt5_module_name,
                                            _dst_module_name,
                                            _uncopyable_classes,
                                            False, #until working with pyqtgraph examples
                                            )
            #make the module available as <dst_pkg>.<module_name>
            setattr(dst_pkg, _module_name, _dst_emulator)
            #also make it directly importable (eg: import <dst_pkg>.QtGui)...
            sys.modules[_dst_module_name] = _dst_emulator
        except ImportError:
            #Not all modules will necessarily be available (eg: QtOpenGL)
            # - well... probably not true now that pkg_util is being used, but
            #    this code ensures we're ok, anyway.
            pass

def get_arg(args, kwargs, arg_index, key, d):
    try:
        ret = args[arg_index]
    except IndexError:
        try:
            ret = kwargs[key]
        except KeyError:
            ret = d
    return ret

def _get_callable_name(func_or_method):
    name = func_or_method.__code__.co_name
    try:
        name = func_or_method.im_class.__name__ + "." + name
    except AttributeError:
        pass
    return name

def _FN_NOT_PORTED_YET_(fn):
    if isinstance(fn, str):
        msg = ("No qt_backport implementation has been created (yet) for '%s'. "
               "There is no pre-existing implementation either.  Please consider "
               "contributing an implementation!" % fn)
        def raise_err():
            raise NotImplementedError(msg)
        return raise_err
    else:
        msg = ("No qt_backport implementation has been created (yet) for '%s'. It "
               "will be called as-is.  If there are issues, please consider "
               "contributing an implementation!" % func_name)
        def call_but_warn(self, *args, **kwargs):
            warnings.warn(msg)
            return fn(*args, **kwargs)
        func_name = _get_callable_name(fn)
        return call_but_warn

def _FN_OBSOLETE_(fn):
    func_name = _get_callable_name(fn)
    def raise_err(self, *args, **kwargs):
        err_msg = ("'%s' is is obsolete with no replacement in Qt5. In addition "
                   "no qt_backport handling has been implemented yet.  Please "
                   "consider contributing an implementation. "
                   "QApplication.setGraphicsSystem() is an example case that has "
                   "specific handling for an obsolete function." % func_name)
        raise NotImplementedError(err_msg)
    return raise_err

def _mv_dict_key(key, src_dict, dst_dict):
    dst_dict[key] = src_dict[key]
    del src_dict[key]

def _copy_qt_module(src_module_name,
                    dst_module_name,
                    uncopyable_class_set,
                    isolate_module_and_classes,
                    ):
    """Returns a copy of the PyQt5 module.  All top level classes in the module
    are also converted to subclasses of the original class in order to enable
    override of class emthods without affecting the original classes from the
    original module.
    
    """
    #Dynamically import the required modules...
    # - the odd fromlist is because without it the __import__ actually
    #    returns the top level module.  See here for a full explanation:
    #       http://stackoverflow.com/questions/2724260
    src_module = __import__(src_module_name, fromlist = ["foo", ])
    
    src_classes = \
        {k: v for k, v in src_module.__dict__.iteritems() \
         if inspect.isclass(v) and k.startswith("Q")}
    
    if not isolate_module_and_classes:
        for k, v in src_classes.iteritems():
            v._qt_root_class = v
        return src_module
    else:
        #The isolation code below works pretty well for basic Qt-based
        #programs, but it is causing pyqtgraph to choke in weird ways. Better
        #to have no isolation for the moment.
        raise NotImplementedError
        
    src_non_classes = \
        {k: v for k, v in src_module.__dict__.iteritems() \
         if k not in src_classes}
    del src_non_classes["__name__"]
    del src_non_classes["__file__"]
    del src_non_classes["__package__"]
    
    #Handle uncopyable classes (make it so we won't subclass them)...
    for fq_cls_name in uncopyable_class_set:
        module_name, cls_name = fq_cls_name.rsplit(".", 1)
        if module_name == src_module_name:
            if cls_name in src_classes:
                _mv_dict_key(cls_name, src_classes, src_non_classes)
    
    #Now subclass each of the src classes so that methods can be overridden
    #without affecting the original source class...
    # - not all of the classes are subclassable (eg: PyQt5.QtCore.pyqtSignal)
    #    so we need exception handling (and care) here
    class_copies = {}
    for cls_name, cls in src_classes.iteritems():
        try:
            #print "about to subclass %s.%s" % (src_module_name, cls_name,),
            class_copies[cls_name] = type(cls_name, (cls, ), {})
            #print "DONE"
        except TypeError:
            print "Can't subclass %s" % cls_name
            pass
        else:
            #stash a reference to the parent class (just in case)
            class_copies[cls_name]._qt_root_class = cls
    
    #class_copies = {k: type(k, (v, ), {}) for k, v in src_classes.iteritems()}
    
    #Make our destination module...
    # - doesn't strictly need to be a module (could be a class), but it may
    #    help in obscure compatibility cases where a module is expected.
    module_docstring = \
        "%s module (dynamically generated as a copy of the %s module)" % \
        (dst_module_name, src_module_name)
    dst_module = type(src_module)(dst_module_name, module_docstring)
    
    #Stuff all the class copies into the module...
    dst_module.__dict__.update(class_copies)
    
    #Add all other module elements...
    dst_module.__dict__.update(src_non_classes)
    dst_module.__file__ = "<autogenerated by %s" % __file__
    return dst_module

def reassemble_QtGui(qt_pkg):
    """Reassembles the dispersed Qt4.QtGui classes"""
    backport_info = {QtWidgets_backports:      qt_pkg.QtWidgets,
                     QtPrintSupport_backports: qt_pkg.QtPrintSupport,
                     QtCore_backports:         qt_pkg.QtCore,
                     QtMultimedia_backports:   qt_pkg.QtMultimedia,
                     QtX11Extras_backports:    qt_pkg.QtX11Extras,
                     }
    for cls_names, module in backport_info.iteritems():
        for cls_name in cls_names:
            cls = getattr(module, cls_name)
            setattr(qt_pkg.QtGui, cls_name, cls)

def _patch_QApplication(qt_pkg):
    cls = qt_pkg.QtWidgets.QApplication
    
    cls.UnicodeUTF8 = _OBSOLETE_
    
    old_translate = cls.translate
    @staticmethod
    def _translate(context, sourceText, disambiguation, *args):
        #all encoding is now done with UTF-8.  See these links:
        # - http://qt-project.org/wiki/Transition_from_Qt_4.x_to_Qt5#3a05fcdc7ec52801754a96d4b80c301b
        # - http://qt-project.org/doc/qt-4.8/qcoreapplication.html#translate
        # - http://qt-project.org/doc/qt-5/qcoreapplication.html#translate
        if len(args) == 2:
            encoding, n = args
            return old_translate(context, sourceText, disambiguation, n)
        elif len(args) == 1:
            #If it is old code calling us it should be encoding. If new code,
            #it would be n. However, we are expecting old code so we will
            #assume it is encoding (and ignore it).  n will be default.
            return old_translate(context, sourceText, disambiguation)
    
    @staticmethod
    def _setGraphicsSystem(gs):
        #setGraphicsSystem has been removed without replacement in Qt5
        # - see http://qt-project.org/forums/viewthread/21785/#124563
        # - rather than failing completely, we will eat the call, but issue
        #    a warning
        warning = ("QApplication.setGraphicsSystem is no longer "
                   "supported in Qt5.  There is no replacement!!")
        warnings.warn(warning)
    
    cls.translate = _translate
    cls.setGraphicsSystem = _setGraphicsSystem
    #cls.setGraphicsSystem = _FN_OBSOLETE_(cls.setGraphicsSystem) #eat instead. See note.

def _patch_QColor(qt_pkg):
    #see http://qt-project.org/doc/qt-5/qcolor-obsolete.html
    cls = qt_pkg.QtGui.QColor
    #Simple renames...
    cls.light = lambda(self): self.lighter() #Qt4.2 -> Qt4.3
    cls.dark  = lambda(self): self.darker()  #Qt4.2 -> Qt4.3

def _patch_QGraphicsItem(qt_pkg):
    #see http://qt-project.org/doc/qt-5/qgraphicsitem-compat.html
    cls = qt_pkg.QtGui.QGraphicsItem
    QTransform = qt_pkg.QtGui.QTransform
    
    def _scale(self, *args, **kwargs):
        if len(args) == 2:
            #see http://goo.gl/pBJqEV
            sx, sy = args
            return self.setTransform(QTransform.fromScale(sx, sy), True)
        else:
            return cls.scale(self, *args, **kwargs)
    
    def _rotate(self, angle):
        #Qt's compatibility docs (linked at the top of this patch call) say
        #to use setRotation(rotation() + angle), but this is not correct.
        #Doing this sets the angle directly without combining together with
        #any existing transformations. The old rotate() *did* work with
        #existing transformations. To be compatibile with this we need to use
        #the transformation system.
        # - see https://bugreports.qt-project.org/browse/QTBUG-39027
        self.setTransform(QTransform().rotate(angle), True)
        
    #simple renames...
    cls.acceptsHoverEvents = lambda self: self.acceptHoverEvents()
    cls.children = lambda self: self.childItems()
    cls.rotate = _rotate
    cls.setAcceptsHoverEvents = lambda self, enabled: self.acceptHoverEvents(enabled)
    
    #fixups...
    cls.scale = _scale
    cls.shear = lambda self, sh, sv: self.setTransform(QTransform().shear(sh, sv), True)
    cls.translate = lambda self, dx, dy: self.setTransform(QTransform.fromTranslate(dx, dy), True);

def _patch_QHeaderView(qt_pkg):
    #see http://qt-project.org/doc/qt-5/qheaderview-compat.html
    cls = qt_pkg.QtWidgets.QHeaderView
    
    def setResizeMode(self, *args): #overloads are (mode, ) and (logicalIndex, mode)
        return self.setSectionResizeMode(*args)
    
    #simple renames...
    cls.isClickable = lambda self: self.sectionsClickable()
    cls.isMovable = lambda self: self.secionsMovable()
    cls.resizeMode = lambda self, logicalIndex: self.sectionResizeMode(logicalIndex)
    cls.setClickable = lambda self, clickable: self.setSectionsClickable(clickable)
    cls.setMovable = lambda self, movable: self.setSectionsMovable(movable)
    cls.setResizeMode = setResizeMode

def _patch_QPainter(qt_pkg):
    #see http://qt-project.org/doc/qt-5/qpainter-obsolete.html
    cls = qt_pkg.QtGui.QPainter
    
    def _drawRoundRect(self, *args, **kwargs):
        arg_types = tuple(type(arg) for arg in args)
        if arg_types[:4] == (int, int, int, int):
            #overload is (x, y, w, h, xRnd, yRnd) or (int * 6)
            x, y, w, h = args[:4]
            xRnd = get_arg(args, kwargs, 4, "xRnd", 25)
            yRnd = get_arg(args, kwargs, 5, "yRnd", 25)
            return self.drawRoundedRect(x, y, w, h, xRnd, yRnd)
        else:
            rect = args[0]
            xRnd = get_arg(args, kwargs, 1, "xRnd", 25)
            yRnd = get_arg(args, kwargs, 2, "yRnd", 25)
            return self.drawRoundedRect(rect, xRnd, yRnd)
    
    old_drawPixmapFragments = cls.drawPixmapFragments
    def _drawPixmapFragments(self, *args, **kwargs):
        #drawPixmapFragments is not implemented the same way anywhere at the
        #moment. See the various docs:
        #   Qt5: http://qt-project.org/doc/qt-5/qpainter.html#drawPixmapFragments
        # PyQt4: http://pyqt.sourceforge.net/Docs/PyQt4/qpainter.html#drawPixmapFragments
        # PyQt5: http://pyqt.sourceforge.net/Docs/PyQt5/api/qpainter.html
        
        if qt_backport.CURRENT_EMULATOR != qt_backport.PYQT4:
            #PySide will likely choke on this, but passing through is better than us
            #raising an error...
            #   - see https://bugreports.qt-project.org/browse/PYSIDE-212
            return old_drawPixmapFragments(self, *args, **kwargs)
        else:
            #PyQt4 has two special APIs for this call
            #  http://pyqt.sourceforge.net/Docs/PyQt4/qpainter.html#drawPixmapFragments
            #
            #As of 2014/05/08 these APIs did not get put into PyQt5. We'll
            #hack around this by looping each fragment. This is inspired by
            #pyqtgraph's PySide workaround.
            #
            #Currently only supporting the overload with this signature:
            #  ([QRectF, ], [QRectF, ], QPixmap, PixmapFragmentHints)
            # 
            if type(args[0][0]) != qt_pkg.QtCore.QRectF:
                #currently only supporting this overload.  Pass through and pray:
                return old_drawPixmapFragments(self, *args, **kwargs)
            else:
                targetRects, sourceRects, pixmap = args[:3]
                #hints = get_arg(args, kwargs, 3, "hints", 0)
                for targetRect, sourceRect in izip(targetRects, sourceRects):
                    cls.drawPixmap(self, targetRect, pixmap, sourceRect)
    
    def _setMatrix(self, matrix, combine):
        #All QMatrix must now be QTransform
        assert isinstance(matrix, qt_pkg.QtGui.QTransform)
        return self.setWorldTransform(matrix, combine)
    
    def _setWorldMatrix(self, matrix, combine):
        #All QMatrix must now be QTransform
        assert isinstance(matrix, qt_pkg.QtGui.QTransform)
        return self.setWorldTransform(matrix, combine)
    
    cls.combinedMatrix = lambda self: self.combinedTransform() #QMatrix is obsolete
    cls.deviceMatrix = lambda self: self.deviceTransform() #QMatrix is obsolete
    cls.drawRoundRect = _drawRoundRect
    cls.drawPixmapFragments = _drawPixmapFragments
    cls.initFrom = lambda self, device: self.begin(device)
    cls.matrix = lambda self: self.worldTransform() #QMatrix is obsolete
    cls.matrixEnabled = lambda self: self.worldMatrixEnabled()
    cls.redirected = _FN_NOT_PORTED_YET_("QPainter.redirected") #Consider QWidget.render
    cls.resetMatrix = _FN_NOT_PORTED_YET_("QPainter.resetMatrix") #convert to QTransform somehow
    cls.restoreRedirected = _FN_NOT_PORTED_YET_("QPainter.restoreRedirected") #Consider QWidget.render
    cls.setMatrix = _setMatrix
    cls.setMatrixEnabled = lambda self, enable: self.setWorldMatrixEnabled(enable)
    cls.setRedirected = _FN_NOT_PORTED_YET_("QPainter.setRedirected") #Consider QWidget.render
    cls.setWorldMatrix = _setWorldMatrix
    cls.worldMatrix = lambda self: self.worldTransform()
    
def _patch_QPainterPath(qt_pkg):
    cls = qt_pkg.QtGui.QPainterPath
    
    old_toSubpathPolygons = cls.toSubpathPolygons
    def toSubpathPolygons(self, *args):
        if len(args) == 0:
            t = qt_pkg.QtGui.QTransform() #identity matrix
            return old_toSubpathPolygons(self, t)
        else:
            return old_toSubpathPolygons(self, *args)
    
    old_toFillPolygons = cls.toFillPolygons
    def toFillPolygons(self, *args):
        if len(args) == 0:
            t = qt_pkg.QtGui.QTransform() #identity matrix
            return old_toFillPolygons(self, t)
        else:
            return old_toFillPolygons(self, *args)
    
    cls.toSubpathPolygons = toSubpathPolygons
    cls.toFillPolygons = toFillPolygons
    
def _patch_QRectF(qt_pkg):
    #see http://qt-project.org/doc/qt-5/qrectf-compat.html
    cls = qt_pkg.QtCore.QRectF
    cls.intersect = lambda self, rectangle: self.intersected(rectangle)
    cls.unite = lambda self, rectangle: self.united(rectangle)

def _patch_QWheelEvent(qt_pkg):
    #see http://qt-project.org/doc/qt-5/qwheelevent-obsolete.html
    cls = qt_pkg.QtGui.QWheelEvent
    
    old_qt5__QWheelEvent_init = cls.__init__
    def _QWheelEvent_init(self, *args, **kwargs):
        #This is tricky... arrival here can be from a few different sources
        #(not including overload issues). The callers can be:
        #   1. PyQt5/Qt5 internal generation
        #        - in this case we get no args or kwargs (unsure why, but this
        #           is what happens), but we have nothing to do since it is
        #           already in Qt5 form.
        #   2. Caller is manually generating the arguments in the old Qt4 way
        #   3. Caller is manually generating the arguments in the old Qt4 way,
        #       but using data from a pre-existgin Qt5 QWheelEvent
        #        - this is a bit of a hybrid case we need to cover
        #        - this case was triggered by pyqtgraph's RemoteGraphicsView
        #Note that, since we are using qt5_backport, we should be able to
        #assume that existing code is using Qt4 conventions (where possible)
        if (args == ()) and (kwargs == {}): #Case 1 above
            #This happens when something internal to PyQt5/Qt5 generated the
            #event. In this case, it is a properly formed event and we don't
            #need to do anything.
            old_qt5__QWheelEvent_init(self, *args, **kwargs)
            return
        else:
            #getting here means someone is creating an event instance manually
            #print "~~~~~`%r %r" % (args, kwargs)
            _qpoint = qt_pkg.QtCore.QPoint     #local space/lookup saver
            _qpointf = qt_pkg.QtCore.QPointF   #local space/lookup saver
            #Do some overload detection and get the args we need...
            if isinstance(args[1], int):
                #we have the old Qt4 overload without globalPos
                orientIdx = 4
                pos, delta, buttons, modifiers = args[:orientIdx]
                globalPos = PyQt5.QtGui.QCursor.pos()
            #elif isinstance(args[1], qt_pkg.QtCore.QPointF)
            else:
                #we have the overload *with* globalPos
                orientIdx = 5
                pos, globalPos, delta, buttons, modifiers = args[:orientIdx]
            try:
                orient = args[orientIdx]
            except IndexError:
                orient = kwargs.get("orient", qt_pkg.QtCore.Qt.Vertical)
            
            #we now have all the potential Qt4 constructor args.  Time to
            #convert them into Qt5-speak...
            if isinstance(pos, _qpoint): #hybrid case
                pos = _qpointf(pos)
            if isinstance(globalPos, _qpoint): #hybrid case
                globalPos = _qpointf(globalPos)

            pixelDelta = _qpoint(0, 0) #determined by experiment
            assert isinstance(delta, int)
            if type(delta) in (_qpoint, _qpointf):
                 #shouldn't happen
                raise Exception("delta should not be a QPoint or QPointF here!")
            elif orient == qt_pkg.QtCore.Qt.Vertical:
                angleDelta = qt_pkg.QtCore.QPoint(0, delta)
            else:
                angleDelta = qt_pkg.QtCore.QPoint(delta, 0)
            qt4Delta = delta
            qt4Orientation = orient
            
            #args are now all fixed up from Qt4->Qt5, so invoke the original...
            args = (self, pos, globalPos, pixelDelta,
                    angleDelta, qt4Delta, qt4Orientation,
                    buttons, modifiers)
            old_qt5__QWheelEvent_init(*args)
        
    def _QWheelEvent_delta(self):
        angle = self.angleDelta()
        return angle.y()
    
    def _QWheelEvent_orientation(self):
        #going with an orthogonal assumption here.  old orientation() docs are
        #less than clear.
        if self.angleDelta().x():
            ret = qt_pkg.QtCore.Qt.Horizontal
        elif self.angleDelta().y():
            ret = qt_pkg.QtCore.Qt.Vertical
        else:
            ret = 0  #old code seems to expect this
        #print "orientation = %r" % ret
        return ret
    
    cls.__init__ = _QWheelEvent_init
    cls.delta       = _QWheelEvent_delta
    cls.orientation = _QWheelEvent_orientation


def patch_api(qt_pkg):
    """Applies patches to make old api usage work with PyQt5.
    
    This does several things:
      1. Implements obsolete api calls on top of new apis
           - eg: QColor.dark() -> QColor.darker()
      2. Adapts call signatures when they changed
           - eg: QApplication.translate() has a new set of args in Qt5.
      3. Whatever else is needed to make old code work!
    
    """
    #Note that Qt5 has several members that are either totally obsolete, or
    #part of the "Qt compatibility layer" that the Qt docs say "We advise
    #against using them in new code.". For the softer advisory warning, PyQt5
    #does not seem to include the "compatibility" members at all. This is
    #fine, but seems to be jumpingthe gun a bit. Since our purpose *is* to
    #deal with old code these members (or many of them) have been added in
    #the patches below.
    _patch_QApplication(qt_pkg)
    _patch_QColor(qt_pkg)
    _patch_QGraphicsItem(qt_pkg)
    _patch_QHeaderView(qt_pkg)
    _patch_QPainter(qt_pkg)
    _patch_QPainterPath(qt_pkg)
    _patch_QRectF(qt_pkg)
    _patch_QWheelEvent(qt_pkg)
    
    #And likely plenty more to come!

