1
2 """
3 Mini Kross - a scripting solution inspired by Kross (http://kross.dipe.org/)
4 Technically this is one of the most important modules in ScripterNG.
5 Via the Qt meta object system it provides access to unwrapped objects.
6 This code uses a lot of metaprogramming magic. To fully understand it,
7 you have to know about metaclasses in Python
8 """
9 from __future__ import with_statement
10 import sip
11 from PyQt4.QtCore import (
12 QMetaObject, Q_RETURN_ARG, QString, Q_ARG,
13 QObject, QVariant, Qt, SIGNAL, QMetaMethod)
14 from PyQt4.QtGui import QBrush, QFont, QPixmap, qApp, QImage, QPalette
15
16
17 variant_converter = {
18
19 "QVariantList": lambda v: from_variantlist(v),
20 "QList<QVariant>": lambda v: v.toList(),
21 "int": lambda v: v.toInt()[0],
22 "double": lambda v: v.toDouble()[0],
23 "char": lambda v: v.toChar(),
24 "QByteArray": lambda v: v.toByteArray(),
25 "QString": lambda v: unicode(v.toString()),
26 "QPoint": lambda v: v.toPoint(),
27 "QPointF": lambda v: v.toPointF(),
28 "QSize": lambda v: v.toSize(),
29 "QLine": lambda v: v.toLine(),
30 "QStringList": lambda v: v.toStringList(),
31 "QTime": lambda v: v.toTime(),
32 "QDateTime": lambda v: v.toDateTime(),
33 "QDate": lambda v: v.toDate(),
34 "QLocale": lambda v: v.toLocale(),
35 "QUrl": lambda v: v.toUrl(),
36 "QRect": lambda v: v.toRect(),
37 "QBrush": lambda v: QBrush(v),
38 "QFont": lambda v: QFont(v),
39 "QPalette": lambda v: QPalette(v),
40 "QPixmap": lambda v: QPixmap(v),
41 "QImage": lambda v: QImage(v),
42 "bool": lambda v: v.toBool(),
43 "QObject*": lambda v: ScripterNG.fromVariant(v),
44 "QWidget*": lambda v: ScripterNG.fromVariant(v),
45 }
50 """
51 convert QList<QVariant> to a normal Python list
52 """
53 return [from_variant(variant) for variant in variantlist.toList()]
54
58 """
59 return real class name
60 Unwrapped classes will be represended in PyQt by a known base class.
61 So obj.__class__.__name__ will not return the desired class name
62 """
63 return obj.metaObject().className()
64
68 """
69 convert a QVariant to a Python value
70 """
71 typeName = variant.typeName()
72 convert = variant_converter.get(typeName)
73 if not convert:
74 raise ValueError, "Could not convert value to %s" % typeName
75 else:
76 return convert(variant)
77
78
79
80 qtclasses = {}
83 """
84 cast a QObject subclass to the best known wrapped super class
85 """
86 if not qtclasses:
87
88
89 import PyQt4
90 qtclasses.update(
91 dict([(key, value) \
92 for key, value in PyQt4.QtCore.__dict__.items() + PyQt4.QtGui.__dict__.items() \
93 if hasattr(value, "__subclasses__") and issubclass(value, QObject)])
94 )
95 try:
96 if not issubclass(value, QObject):
97 return obj
98 except TypeError:
99
100 return obj
101 mo = obj.metaObject()
102 while mo:
103 cls = qtclasses.get(str(mo.className()))
104 if cls:
105 return sip.cast(obj, cls)
106 mo = mo.superClass()
107
108 return obj
109
110
111
112 -def wrap(obj, force=False):
113 """
114 If a class is not known by PyQt it will be automatically
115 casted to a known wrapped super class.
116 But that limits access to methods and propperties of this super class.
117 So instead this functions returns a wrapper class (PyQtClass)
118 which queries the metaObject and provides access to
119 all slots and all properties.
120 """
121 if isinstance(obj, QString):
122
123 return unicode(obj)
124 elif isinstance(obj, PyQtClass):
125
126 return obj
127 if obj and isinstance(obj, QObject):
128 if force or obj.__class__.__name__ != obj.metaObject().className():
129
130 obj = create_pyqt_object(obj)
131 return obj
132
136 """
137 checks if a object is wrapped by PyQtClass
138 """
139
140 return hasattr(obj, "qt")
141
145 """
146 if wrapped returns the wrapped object
147 """
148 if is_wrapped(obj):
149 obj = obj.qt
150 return obj
151
155 """
156 checks if class or wrapped class is a subclass of QObject
157 """
158 if hasattr(obj, "__bases__") and issubclass(unwrap(obj), QObject):
159 return True
160 else:
161 return False
162
165 """
166 walk up the object tree until ScripterNG or the root is found
167 """
168 found = False
169 p = qobj.parent()
170 while p and not found:
171 if str(p.objectName()) == "ScripterNG":
172 found = True
173 break
174 else:
175 p = p.parent()
176 return found
177
178
179
180 -class Error(Exception):
181 """
182 Base error classed. Catch this to handle exceptions comming from C++
183 """
184
188 """
189 Base class
190 """
191
193 self._instance = instance
194
195
197 """
198 If this object is deleted it should also delete the wrapped object
199 if it was created explicitly for this use.
200 """
201 qobj = self._instance
202 if is_scripterng_child(qobj):
203 if len(qobj.children()):
204 print "Cannot delete", qobj, "because it has child objects"
205
206
207
208 sip.delete(qobj)
209
210
211
212
215
216
218 return wrap(self._instance.property(name))
219
220
222 return self.__class__.__properties__.keys()
223
224
227
228
231
232
234 self._instance.connect(self._instance, SIGNAL(signal), slot)
235
236
238 self._instance.disconnect(self._instance, SIGNAL(signal), slot)
239
240
243
244
247
248
249 @property
251 return self._instance
252
253
255 if isinstance(key, int):
256 length = getattr(self, "length", None)
257 if length is not None:
258
259 try:
260 return getattr(self, str(key))
261 except AttributeError, e:
262 raise IndexError, key
263 else:
264 return self.children()[key]
265 else:
266 return getattr(self, key)
267
268
270
271 for child in self._instance.children():
272 if str(child.objectName()) == name:
273 obj = wrap(child)
274
275 setattr(self, name, obj)
276 return obj
277
278 variant = self._instance.property(name)
279 if variant.type() != 0:
280 return from_variant(variant)
281 raise AttributeError, name
282
283
284 @property
286 """
287 This method is for introspection.
288 Using dir(thispyqtclass_object) returns a list of
289 all children, methods, properties and dynamic properties.
290 """
291 names = self.__dict__.keys()
292 for c in self._instance.children():
293 child_name = str(c.objectName())
294 if child_name:
295 names.append(child_name)
296
297 for pn in self._instance.dynamicPropertyNames():
298 names.append(str(pn))
299 return names
300
301
303 print "__enter__", self
304
305
306 - def __exit__(self, exc_type, exc_value, traceback):
307 print "__exit__", self, exc_type, exc_value, traceback
308
313
314
315 __slots__ = ["meta_property", "name", "__doc__", "read_only"]
316
317
326
327
328 - def get(self, obj):
330
331
332 - def set(self, obj, value):
334
339
340 __slots__ = ["meta_method", "name", "args", "returnType", "__doc__"]
341
342
344 self.meta_method = meta_method
345 self.name, args = str(meta_method.signature()).split("(", 1)
346 self.args = args[:-1].split(",")
347 self.returnType = str(meta_method.typeName())
348
349 types = [str(t) for t in meta_method.parameterTypes()]
350 names = [str(n) or "arg%i" % (i+1) \
351 for i, n in enumerate(meta_method.parameterNames())]
352 params = ", ".join("%s %s" % (t, n) for n, t in zip(types, names))
353
354 self.__doc__ = "%s(%s)%s" % (
355 self.name, params,
356 self.returnType and (" -> %s" % self.returnType) or ""
357 )
358
359
361 def wrapper(obj, *args):
362
363 qargs = [Q_ARG(t, v) for t, v in zip(self.args, args)]
364 invoke_args = [obj._instance, self.name]
365 invoke_args.append(Qt.DirectConnection)
366 rtype = self.returnType
367 if rtype:
368 invoke_args.append(Q_RETURN_ARG(rtype))
369 invoke_args.extend(qargs)
370 try:
371 result = QMetaObject.invokeMethod(*invoke_args)
372 error_msg = str(qApp.property("MIKRO_EXCEPTION").toString())
373 if error_msg:
374
375 qApp.setProperty("MIKRO_EXCEPTION", QVariant())
376 raise Error(error_msg)
377 except RuntimeError, e:
378 raise TypeError, \
379 "%s.%s(%r) call failed: %s" % (obj, self.name, args, e)
380 return wrap(result)
381 wrapper.__doc__ = self.__doc__
382 return wrapper
383
392 class_name = str(metaobject.className())
393 cls = pyqt_classes.get(class_name)
394 if cls:
395 return cls
396 attrs = {}
397
398 properties = attrs["__properties__"] = {}
399 for i in range(metaobject.propertyCount()):
400 prop = PyQtProperty(metaobject.property(i))
401 prop_name = str(prop.name)
402
403 if prop.read_only:
404
405 properties[prop_name] = attrs[prop_name] = property(prop.get, doc=prop.__doc__)
406 else:
407 properties[prop_name] = attrs[prop_name] = property(
408 prop.get, prop.set, doc=prop.__doc__)
409
410 methods = attrs["__methods__"] = {}
411 for i in range(metaobject.methodCount()):
412 meta_method = metaobject.method(i)
413 if meta_method.methodType() != QMetaMethod.Signal:
414 method = PyQtMethod(meta_method)
415 method_name = method.name
416 if method_name in attrs:
417
418
419 method_name += "_"
420 instance_method = method.instancemethod()
421 instance_method.__doc__ = method.__doc__
422 methods[method_name] = attrs[method_name] = instance_method
423
424
425
426 cls = type(class_name, (PyQtClass,), attrs)
427 pyqt_classes[class_name] = cls
428 return cls
429
433 """
434 Wrap a QObject and make all slots and properties dynamically available.
435 @type obj: QObject
436 @param obj: an unwrapped QObject
437 @rtype: PyQtClass object
438 @return: dynamicaly created object with all available properties and slots
439
440 This is probably the only function you need from this module.
441 Everything else are helper functions and classes.
442 """
443 cls = create_pyqt_class(obj.metaObject())
444 return cls(obj)
445