import errno import os import re from string import Template PYTHONQT_WRAPPER_WITH_PARENT = Template(""" //----------------------------------------------------------------------------- class PythonQtWrapper_${className} : public QObject { Q_OBJECT public: public Q_SLOTS: ${className}* new_${className}(${parentClassName}* parent = 0) { return new ${className}(parent); } void delete_${className}(${className}* obj) { delete obj; } }; """) PYTHONQT_WRAPPER_WITHOUT_PARENT = Template(""" //----------------------------------------------------------------------------- class PythonQtWrapper_${className} : public QObject { Q_OBJECT public: public Q_SLOTS: ${className}* new_${className}() { return new ${className}(); } void delete_${className}(${className}* obj) { delete obj; } }; """) def _mkdir_p(path): """See """ try: os.makedirs(path) except OSError as exc: if exc.errno == errno.EEXIST and os.path.isdir(path): pass else: raise def ctk_wrap_pythonqt(target, namespace, output_dir, input_files, extra_verbose): if extra_verbose: print("target: %s" % target) print("namespace: %s" % namespace) print("output_dir: %s" % output_dir) print("input_files: %s" % input_files) _mkdir_p(output_dir) includes = [] pythonqtWrappers = [] registerclasses = [] namespace = namespace.replace('.', '_') for input_file in input_files: filename = os.path.basename(input_file) if extra_verbose: print("Wrapping %s" % filename) # what is the filename without the extension filename_we = os.path.splitext(filename)[0] # Extract classname - NOTE: We assume the filename matches the associated class className = filename_we if extra_verbose: print("\tclassName:%s" % className) # Extract parent classname parentClassName = None # Read input files with open(input_file) as f: content = f.read() # Skip wrapping if file do NOT contain Q_OBJECT if 'Q_OBJECT' not in content: if extra_verbose: print("\tskipping - No Q_OBJECT macro") continue # Skip wrapping if constructor doesn't match: # my_class() # my_class(QObject* newParent ...) # my_class(QWidget* newParent ...) # Constructor with either QWidget or QObject as first parameter regex = r"[^~]%s[\s\n]*\([\s\n]*((QObject|QWidget)[\s\n]*\*[\s\n]*\w+[\s\n]*(\=[\s\n]*(0|NULL)|,.*\=.*\)|\)|\)))" % className res = re.search(regex, content, re.MULTILINE) if res is None: if extra_verbose: print("\tskipping - Missing expected constructor signature") continue # Skip wrapping if object has a virtual pure method # "x3b" is the unicode for semicolon regex = r"virtual[\w\n\s\*\(\)]+\=[\s\n]*(0|NULL)[\s\n]*\x3b" res = re.search(regex, content, re.MULTILINE) if res is not None: if extra_verbose: print("skipping - Contains a virtual pure method") continue if parentClassName is None: # Does constructor signature is of the form: myclass() regex = r"[^~]%s[\s\n]*\([\s\n]*\)" % className res = re.search(regex, content, re.MULTILINE) if res is not None: parentClassName = "" if extra_verbose: print("\tconstructor of the form: %s()" % className) if parentClassName is None: # Does constructor signature is of the form: myclass(QObject * parent ...) regex = r"%s[\s\n]*\([\s\n]*QObject[\s\n]*\*[\s\n]*\w+[\s\n]*(\=[\s\n]*(0|NULL)|,.*\=.*\)|\))" % className res = re.search(regex, content, re.MULTILINE) if res is not None: parentClassName = "QObject" if extra_verbose: print("\tconstructor of the form: %s(QObject * parent ... )" % className) if parentClassName is None: # Does constructor signature is of the form: myclass(QWidget * parent ...) regex = r"%s[\s\n]*\([\s\n]*QWidget[\s\n]*\*[\s\n]*\w+[\s\n]*(\=[\s\n]*(0|NULL)|,.*\=.*\)|\))" % className res = re.search(regex, content, re.MULTILINE) if res is not None: parentClassName = "QWidget" if extra_verbose: print("\tconstructor of the form: %s(QWidget * parent ... )" % className) if parentClassName is not None: includes.append('#include "%s.h"' % filename_we) # Generate PythonQtWrapper class if parentClassName == "QObject" or parentClassName == "QWidget": pythonqtWrappers.append( PYTHONQT_WRAPPER_WITH_PARENT.substitute(className = className, parentClassName = parentClassName)) elif parentClassName == "": pythonqtWrappers.append(PYTHONQT_WRAPPER_WITHOUT_PARENT.substitute(className = className)) else: # Case parentClassName is None raise Exception("Problem wrapping %s" % input_file) # Generate code allowing to register the class metaobject and its associated "light" wrapper registerclasses.append( Template(""" PythonQt::self()->registerClass( &${className}::staticMetaObject, "${target}", PythonQtCreateObject); """).substitute(className = className, target = target)) output_header = output_dir + "/" + namespace + "_" + target + ".h" if extra_verbose: print("output_header: %s" % output_header) # Write master include file with open(output_header, "w") as f: f.write(Template( """ // // File auto-generated by ctkWrapPythonQt.py // #ifndef __${namespace}_${target}_h #define __${namespace}_${target}_h #include ${includes} ${pythonqtWrappers} #endif """).substitute(namespace = namespace, target = target, includes = '\n'.join(includes), pythonqtWrappers = '\n'.join(pythonqtWrappers))) output_cpp = output_dir + "/" + namespace + "_" + target + "_init.cpp" if extra_verbose: print("output_cpp: %s" % output_cpp) with open(output_cpp , "w") as f: # Write wrapper header f.write(Template( """ // // File auto-generated by ctkWrapPythonQt.py // #include // XXX Avoid warning: "HAVE_XXXX" redefined #undef HAVE_STAT #undef HAVE_FTIME #undef HAVE_GETPID #undef HAVE_IO_H #undef HAVE_STRERROR #undef HAVE_SYS_UTIME_H #undef HAVE_TEMPNAM #undef HAVE_TMPNAM #undef HAVE_LONG_LONG #undef HAVE_INT64_T #include "${namespace}_${target}.h" void PythonQt_init_${namespace}_${target}(PyObject* module) { Q_UNUSED(module); ${registerclasses} } """).substitute(namespace = namespace, target = target, registerclasses = '\n'.join(registerclasses))) if __name__ == '__main__': from optparse import OptionParser usage = "usage: %prog [options] [ [...]]" parser = OptionParser(usage=usage) parser.add_option("-t", "--target", dest="target", action="store", type="string", help="Name of the associated library") parser.add_option("-n", "--namespace", dest="namespace", action="store", type="string", help="Wrapping namespace") parser.add_option("--output-dir", dest="output_dir", action="store", type="string", help="Output directory") parser.add_option("-v", "--verbose", dest="verbose", action="store_true", help="Print verbose information") parser.add_option("--extra-verbose", dest="extra_verbose", action="store_true", help="Print extra verbose information") (options, args) = parser.parse_args() #if len(args) < 2: # parser.error("arguments '%s' are required !" % ' ') if options.extra_verbose: options.verbose = True ctk_wrap_pythonqt(options.target, options.namespace, options.output_dir, args, options.extra_verbose) if options.verbose: print("Wrapped %d files" % len(args))