CTK  0.1.0
The Common Toolkit is a community effort to provide support code for medical image analysis, surgical navigation, and related projects.
ctkWrapPythonQt.py
Go to the documentation of this file.
1 
2 import errno
3 import os
4 import re
5 from string import Template
6 
7 PYTHONQT_WRAPPER_WITH_PARENT = Template("""
8 //-----------------------------------------------------------------------------
9 class PythonQtWrapper_${className} : public QObject
10 {
11 Q_OBJECT
12 public:
13 public 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 
22 PYTHONQT_WRAPPER_WITHOUT_PARENT = Template("""
23 //-----------------------------------------------------------------------------
24 class PythonQtWrapper_${className} : public QObject
25 {
26 Q_OBJECT
27 public:
28 public Q_SLOTS:
29  ${className}* new_${className}()
30  {
31  return new ${className}();
32  }
33  void delete_${className}(${className}* obj) { delete obj; }
34 };
35 """)
36 
37 def _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 
46 def 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 
202 void 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 
209 if __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 ctk_wrap_pythonqt(target, namespace, output_dir, input_files, extra_verbose)