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