|
@@ -0,0 +1,228 @@
|
|
|
+
|
|
|
+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]
|
|
|
+
|
|
|
+ includes.append('#include "%s.h"' % filename_we)
|
|
|
+
|
|
|
+ # 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)
|
|
|
+
|
|
|
+ # 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<PythonQtWrapper_${className}>);
|
|
|
+ """).substitute(className = className, target = target))
|
|
|
+
|
|
|
+ output_header = output_dir + "/" + namespace + "_" + target + "0.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}0_h
|
|
|
+#define __${namespace}_${target}0_h
|
|
|
+
|
|
|
+#include <QWidget>
|
|
|
+${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 <PythonQt.h>
|
|
|
+#include "${namespace}_${target}0.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] <output_file> <input_file> [<input_file1> [...]]"
|
|
|
+ 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 !" % '<output_file> <input_file>')
|
|
|
+
|
|
|
+ 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))
|