CTK 0.1.0
The Common Toolkit is a community effort to provide support code for medical image analysis, surgical navigation, and related projects.
Loading...
Searching...
No Matches
ctkWrapPythonQt.py
Go to the documentation of this file.
2import errno
3import os
4import re
5from string import Template
6
7PYTHONQT_WRAPPER_WITH_PARENT = Template("""
8//-----------------------------------------------------------------------------
9class PythonQtWrapper_${className} : public QObject
10{
11Q_OBJECT
12public:
13public Q_SLOTS:
14 ${className}* new_${className}(${parentClassName}* parent = 0)
15 {
16 return new ${className}(parent);
17 }
18 void delete_${className}(${className}* obj) { delete obj; }
19};
20""")
21
22PYTHONQT_WRAPPER_WITHOUT_PARENT = Template("""
23//-----------------------------------------------------------------------------
24class PythonQtWrapper_${className} : public QObject
25{
26Q_OBJECT
27public:
28public Q_SLOTS:
29 ${className}* new_${className}()
30 {
31 return new ${className}();
32 }
33 void delete_${className}(${className}* obj) { delete obj; }
34};
35""")
36
37def _mkdir_p(path):
38 """See """
39 try:
40 os.makedirs(path)
41 except OSError as exc:
42 if exc.errno == errno.EEXIST and os.path.isdir(path):
43 pass
44 else: raise
45
46def ctk_wrap_pythonqt(target, namespace, output_dir, input_files, extra_verbose):
47 if extra_verbose:
48 print("target: %s" % target)
49 print("namespace: %s" % namespace)
50 print("output_dir: %s" % output_dir)
51 print("input_files: %s" % input_files)
52
53 _mkdir_p(output_dir)
54
55 includes = []
56 pythonqtWrappers = []
57 registerclasses = []
58 namespace = namespace.replace('.', '_')
59
60 for input_file in input_files:
61 filename = os.path.basename(input_file)
62 if extra_verbose:
63 print("Wrapping %s" % filename)
64
65 # what is the filename without the extension
66 filename_we = os.path.splitext(filename)[0]
67
68 # Extract classname - NOTE: We assume the filename matches the associated class
69 className = filename_we
70 if extra_verbose:
71 print("\tclassName:%s" % className)
72
73 # Extract parent classname
74 parentClassName = None
75
76 # Read input files
77 with open(input_file) as f:
78 content = f.read()
79
80 # Skip wrapping if file do NOT contain Q_OBJECT
81 if 'Q_OBJECT' not in content:
82 if extra_verbose:
83 print("\tskipping - No Q_OBJECT macro")
84 continue
85
86 # Skip wrapping if constructor doesn't match:
87 # my_class()
88 # my_class(QObject* newParent ...)
89 # my_class(QWidget* newParent ...)
90 # Constructor with either QWidget or QObject as first parameter
91 regex = r"[^~]%s[\s\n]*\‍([\s\n]*((QObject|QWidget)[\s\n]*\*[\s\n]*\w+[\s\n]*(\=[\s\n]*(0|NULL)|,.*\=.*\‍)|\‍)|\‍)))" % className
92 res = re.search(regex, content, re.MULTILINE)
93 if res is None:
94 if extra_verbose:
95 print("\tskipping - Missing expected constructor signature")
96 continue
97
98 # Skip wrapping if object has a virtual pure method
99 # "x3b" is the unicode for semicolon
100 regex = r"virtual[\w\n\s\*\‍(\‍)]+\=[\s\n]*(0|NULL)[\s\n]*\x3b"
101 res = re.search(regex, content, re.MULTILINE)
102 if res is not None:
103 if extra_verbose:
104 print("skipping - Contains a virtual pure method")
105 continue
106
107 if parentClassName is None:
108 # Does constructor signature is of the form: myclass()
109 regex = r"[^~]%s[\s\n]*\‍([\s\n]*\‍)" % className
110 res = re.search(regex, content, re.MULTILINE)
111
112 if res is not None:
113 parentClassName = ""
114 if extra_verbose:
115 print("\tconstructor of the form: %s()" % className)
116
117 if parentClassName is None:
118 # Does constructor signature is of the form: myclass(QObject * parent ...)
119 regex = r"%s[\s\n]*\‍([\s\n]*QObject[\s\n]*\*[\s\n]*\w+[\s\n]*(\=[\s\n]*(0|NULL)|,.*\=.*\‍)|\‍))" % className
120 res = re.search(regex, content, re.MULTILINE)
121 if res is not None:
122 parentClassName = "QObject"
123 if extra_verbose:
124 print("\tconstructor of the form: %s(QObject * parent ... )" % className)
125
126 if parentClassName is None:
127 # Does constructor signature is of the form: myclass(QWidget * parent ...)
128 regex = r"%s[\s\n]*\‍([\s\n]*QWidget[\s\n]*\*[\s\n]*\w+[\s\n]*(\=[\s\n]*(0|NULL)|,.*\=.*\‍)|\‍))" % className
129 res = re.search(regex, content, re.MULTILINE)
130 if res is not None:
131 parentClassName = "QWidget"
132 if extra_verbose:
133 print("\tconstructor of the form: %s(QWidget * parent ... )" % className)
134
135 if parentClassName is not None:
136 includes.append('#include "%s.h"' % filename_we)
137
138 # Generate PythonQtWrapper class
139 if parentClassName == "QObject" or parentClassName == "QWidget":
140 pythonqtWrappers.append(
141 PYTHONQT_WRAPPER_WITH_PARENT.substitute(className = className, parentClassName = parentClassName))
142
143 elif parentClassName == "":
144 pythonqtWrappers.append(PYTHONQT_WRAPPER_WITHOUT_PARENT.substitute(className = className))
145
146 else: # Case parentClassName is None
147 raise Exception("Problem wrapping %s" % input_file)
148
149 # Generate code allowing to register the class metaobject and its associated "light" wrapper
150 registerclasses.append(
151 Template("""
152 PythonQt::self()->registerClass(
153 &${className}::staticMetaObject, "${target}",
154 PythonQtCreateObject<PythonQtWrapper_${className}>);
155 """).substitute(className = className, target = target))
156
157 output_header = output_dir + "/" + namespace + "_" + target + ".h"
158 if extra_verbose:
159 print("output_header: %s" % output_header)
160 # Write master include file
161 with open(output_header, "w") as f:
162 f.write(Template(
163"""
164//
165// File auto-generated by ctkWrapPythonQt.py
166//
167
168#ifndef __${namespace}_${target}_h
169#define __${namespace}_${target}_h
170
171#include <QObject>
172${includes}
173${pythonqtWrappers}
174#endif
175""").substitute(namespace = namespace, target = target, includes = '\n'.join(includes), pythonqtWrappers = '\n'.join(pythonqtWrappers)))
176
177 output_cpp = output_dir + "/" + namespace + "_" + target + "_init.cpp"
178 if extra_verbose:
179 print("output_cpp: %s" % output_cpp)
180 with open(output_cpp , "w") as f:
181 # Write wrapper header
182 f.write(Template(
183"""
184//
185// File auto-generated by ctkWrapPythonQt.py
186//
187
188#include <PythonQt.h>
189// XXX Avoid warning: "HAVE_XXXX" redefined
190#undef HAVE_STAT
191#undef HAVE_FTIME
192#undef HAVE_GETPID
193#undef HAVE_IO_H
194#undef HAVE_STRERROR
195#undef HAVE_SYS_UTIME_H
196#undef HAVE_TEMPNAM
197#undef HAVE_TMPNAM
198#undef HAVE_LONG_LONG
199#undef HAVE_INT64_T
200#include "${namespace}_${target}.h"
201
202void PythonQt_init_${namespace}_${target}(PyObject* module)
203{
204 Q_UNUSED(module);
205 ${registerclasses}
206}
207""").substitute(namespace = namespace, target = target, registerclasses = '\n'.join(registerclasses)))
208
209if __name__ == '__main__':
210 from optparse import OptionParser
211 usage = "usage: %prog [options] <output_file> <input_file> [<input_file1> [...]]"
212 parser = OptionParser(usage=usage)
213 parser.add_option("-t", "--target",
214 dest="target", action="store", type="string",
215 help="Name of the associated library")
216 parser.add_option("-n", "--namespace",
217 dest="namespace", action="store", type="string",
218 help="Wrapping namespace")
219 parser.add_option("--output-dir",
220 dest="output_dir", action="store", type="string",
221 help="Output directory")
222 parser.add_option("-v", "--verbose",
223 dest="verbose", action="store_true",
224 help="Print verbose information")
225 parser.add_option("--extra-verbose",
226 dest="extra_verbose", action="store_true",
227 help="Print extra verbose information")
228
229 (options, args) = parser.parse_args()
230
231 #if len(args) < 2:
232 # parser.error("arguments '%s' are required !" % '<output_file> <input_file>')
233
234 if options.extra_verbose:
235 options.verbose = True
236
237 ctk_wrap_pythonqt(options.target, options.namespace, options.output_dir, args, options.extra_verbose)
238
239 if options.verbose:
240 print("Wrapped %d files" % len(args))
def _mkdir_p(path)
def ctk_wrap_pythonqt(target, namespace, output_dir, input_files, extra_verbose)