Skip to content

Instantly share code, notes, and snippets.

@uranusjr
Last active August 29, 2015 14:12
Show Gist options
  • Save uranusjr/69e39e75826e3817350b to your computer and use it in GitHub Desktop.
Save uranusjr/69e39e75826e3817350b to your computer and use it in GitHub Desktop.
Testing black magic
# ↓↓↓ Scroll to bottom for an example. ↓↓↓
import importlib
import pkgutil
def pythonize(qt_package):
proxy = _PackageProxy(qt_package)
return proxy
def _make_class_proxy(qt_klass):
class ClassProxy(object):
def __init__(self, *args, **kwargs):
super(ClassProxy, self).__init__()
self.__backend = qt_klass(*args, **kwargs)
def __getattr__(self, name):
# Convert Pythonic attribute names to camel case names.
if name[0].islower():
# this_is_an_attribute => thisIsAnAttribute
comps = name.split('_')
comps = comps[:1] + [c.title() for c in comps[1:]]
else:
# THIS_IS_A_CONSTANT => ThisIsAConstant
comps = [c.title() for c in name.split('_')]
name = ''.join(comps)
return getattr(self.__backend, name)
return ClassProxy
class _ModuleProxy(object):
def __init__(self, module):
super(_ModuleProxy, self).__init__()
self.module = module
def __getattr__(self, name):
try:
qt_klass = getattr(self.module, name)
except AttributeError:
qt_klass = getattr(self.module, 'Q' + name)
proxy = _make_class_proxy(qt_klass)
return proxy
class _PackageProxy(object):
def __init__(self, package):
prefix = package.__name__ + '.'
path = package.__path__
for _, full_name, _ in pkgutil.iter_modules(path, prefix):
name = full_name.split('.')[-1]
if name.startswith('Qt'):
# Modules like QtCore, QtGui, QtWidgets, etc.
# Strip "Qt" prefix, convert to lower (i.e. core, gui, widgets)
# Note that multiword modules won't contain underscores. For
# example "QtPrintSupport" will become "printsupport".
local_name = name[2:].lower()
elif name[0].isupper():
# Modules without the "Qt" prefix (e.g. Enginio)
local_name = name.lower()
elif name[0] == '_':
# Private module. Skip.
continue
else:
# Normal packages. Use as-is.
local_name = name
module = importlib.import_module(full_name)
proxy = _ModuleProxy(module)
# Inject this into the local scope.
setattr(self, local_name, proxy)
# I'm testing with PyQt5. Rewrite where appropriate.
import sys
import PyQt5
qt = pythonize(PyQt5)
app = qt.widgets.Application(sys.argv)
w = qt.widgets.MainWindow()
w.show()
app.exec_()
  • Use metaclass instead of class wrapper to avoid breaking the inheritance chain.
  • Map global functions and macros (e.g. qApp, QT_VERSION, etc.)
  • Special-case things in the Qt global namespace (PyQt5.Qt). There are a lot of things in it, including aliases to all public classes and constants.
  • Python-specific things like pyqtSignal (and similar things in PySide).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment