Commits

Henning Schröder committed 29d4efb

added stub generator

Comments (0)

Files changed (3)

 t = Test()
 t.test()
 print t.calc(1,2)
+
+from pyqtstubs import StubCreator
+stubs = StubCreator()
+stubs.add_class(embpyqt._qt.__class__)
+stubs.add_class(t._qt.__class__)
+print stubs
+
 embpyqt.setException("foo", "baz")
+
+
 print "done"
 # -*- coding: utf-8 -*-
 import sys
 
-from PyQt4.QtCore import QMetaMethod, pyqtSlot, QObject, QVariant
+from PyQt4.QtCore import QMetaMethod, pyqtSlot, QObject, QVariant, QString
 
 
 
 
     
     def _matching_method_for_signature(self, arg_types):
+        found = None
         for i, (signature, method) in enumerate(self.methods.items()):
-            pass
-        return method
+            found = method
+            break # XXX: add real code here
+        if not found:
+            raise TypeError("No matching methods with given signature found")
+        return found
 
 
     def __call__(self, *args, **kwargs):
         result = call(*args)
         if isinstance(result, QObject):
             return wrap_instance(result)
+        elif isinstance(result, QString):
+            return unicode(result)
         return result
 
 
     def __init__(self, meta_property):
         self._mp = meta_property
         name = self._mp.name()
+        self.qt_name = name
         self.name = name[0].upper() + name[1:]
-        self.type = self._mp.type()
+        self.type = self._mp.typeName()
 
         
     def __get__(self, obj, cls):
         value = self._mp.read(obj)
         if isinstance(value, QObject):
             return wrap_instance(value)
+        elif isinstance(value, QString):
+            return unicode(value)
         return value
 
     
     def __init__(self, *args, **kwargs):
         instance = kwargs.pop("_wrap_instance", None)
         if not instance:
-            mo = self.__class__.__dict__["__metaobject"]
+            mo = self.__class__.__dict__["_metaobject"]
             instance = mo.newInstance(*args, **kwargs)
         self._qt = instance
 
         
     def __del__(self):
-        print self, "deleted"
+        pass #print self, "deleted"
+
 
     def __repr__(self):
-        mo = self.__class__.__dict__["__metaobject"]
-        return "<%s object>" % mo.className()
+        mo = self.__class__.__dict__["_metaobject"]
+        return "<%s object %r>" % (mo.className(), unicode(self._qt.objectName()))
 
     
     def __iter__(self):
     def __getattr__(self, name):
         # dynamic properties cannot be wrapped with QtProperty descriptors
         pnames = [unicode(n) for n in self._qt.dynamicPropertyNames()]
-        if name.lower() in pnames:
-            return self._qt.property(name.lower()).toPyObject()
+        qname = name[0].lower() + name[1:]
+        if qname in pnames:
+            value = self._qt.property(qname).toPyObject()
+            if isinstance(value, QObject):
+                return wrap_instance(value)
+            elif isinstance(result, QString):
+                return unicode(result)
+            return result
+
         # access child objects as attribute
         for child in self._qt.children():
             if child.objectName() == name:
         attrs = {}
     for k, v in properties.items():
         attrs[k] = v
-    attrs["__properties"] = properties
+    attrs["_properties"] = properties
     for k, v in methods.items():
         attrs[k] = QtMethodDispatcher(k, v)
-    attrs["__methods"] = methods
-    attrs["__metaobject"] = meta_object
+    attrs["_methods"] = methods
+    attrs["_metaobject"] = meta_object
     class_name = mo.className()
     return type(class_name, bases, attrs)
 
 
 
 if __name__ == "__main__":
+    from PyQt4.QtCore import QObject
     from PyQt4.QtGui import QApplication, QPushButton
     _a = QApplication(sys.argv)
     a = wrap_instance(_a)
     b.Text = "Click to close!"
     b.clicked.connect(on_click)
     
-    cls = class_from_metaobject(QPushButton.staticMetaObject)
+    cls = class_from_metaobject(QObject.staticMetaObject)
     print cls
     t = cls()
-    t.show()
+    t.ObjectName = "foo bar"
+    print t
+# -*- coding: utf-8 -*-
+import keyword
+
+from PyQt4.QtCore import QMetaMethod
+from pyqtproxy import class_from_metaobject
+
+
+
+class CodeGenerator(object):
+	
+	
+	def __init__(self):
+		self.output = []
+		self.level = 0
+
+	
+	def indent(self, count=1):
+		self.level += count
+	
+		
+	def dedent(self, count=1):
+		self.level -= count
+	
+	
+	def begin(self, code=""):
+		if code:
+			self.emit(code)
+		self.indent()
+
+	
+	def end(self):
+		if self.output and self.output[-1] and self.output[-1][-1] == ":":
+			self.emit("pass")
+		self.dedent()
+
+
+	def emit(self, code):
+		indention = "  " * self.level
+		self.output.append(indention + code)
+
+
+	def prepend_header(self, code):
+		if code not in self.output:
+			self.output.insert(0, code)
+
+		
+	def newline(self, count=1):
+		self.output.extend([""]*count)
+
+
+	def __str__(self):
+		return "\n".join(self.output)
+	
+
+
+class StubCreator(object):
+
+	
+	def __init__(self):
+		self.codegen = CodeGenerator()
+		self.codegen.newline()
+
+	def add_class(self, cls):
+		pcls = class_from_metaobject(cls.staticMetaObject)
+		self.codegen.newline()
+		self.codegen.begin("class %s(object):" % pcls._metaobject.className())
+		self.codegen.newline()
+		qt_types = set()
+		# -- methods --
+		for method_name, methods in pcls._methods.items():
+			if method_name.startswith("_"):
+				continue # skip private methods
+			for signature, method in methods.items():
+				param_names = map(unicode, method.param_names)
+				param_types = map(unicode, method.param_types)
+				for i, (pn, pt) in enumerate(zip(param_names, param_types)):
+					pt = self.fix_type(pt)
+					if pt.startswith("Q"):
+						qt_types.add(pt)
+					param_types[i] = pt
+					if not pn:
+						pn = "%s_%s" % (pt, i)
+						param_names[i] = pn
+				args = ["self"] + param_names
+				signature = ", ".join(args)
+				if method.method_type == QMetaMethod.Signal:
+					self.codegen.emit("%s = pyqtSignal(%s)" % (method_name, ", ".join(param_types)))
+				else:
+					self.codegen.begin("def %s(%s):" % (self.fix_name(method_name), signature))
+					for pname, ptype in zip(param_names, param_types):
+						self.codegen.emit("assert isinstance(%s, %s)" % (pname, ptype))
+					
+					rtype = unicode(method.return_type)
+					if rtype not in ("void", ""):
+						rtype = self.fix_type(rtype)
+						if rtype.startswith("Q"):
+							qt_types.add(rtype)
+						self.codegen.emit("return %s()" % rtype)
+					self.codegen.end()
+				self.codegen.newline()
+				break
+		# -- properties --
+		for property_name, prop in pcls._properties.items():
+			ptype = self.fix_type(prop.type)
+			if ptype.startswith("Q"):
+				qt_types.add(ptype)
+
+			getter = pcls._methods.get(prop.qt_name, None)
+			setter = pcls._methods.get("set%s" % property_name, None)
+			if getter and setter:
+				self.codegen.emit("%s = property(fget=%s, fset=set%s)" % (property_name, prop.qt_name, property_name))
+			elif getter:
+				self.codegen.emit("%s = property(fget=%s)" % (property_name, prop.qt_name))
+			elif setter:
+				self.codegen.emit("%s = property(fset=set%s)" % (property_name, property_name))
+			else:
+				self.codegen.emit("%s = property()" % property_name)
+
+		self.codegen.newline()
+		self.codegen.begin("def __assert_property_types(self):")
+		self.codegen.emit("# gives source code analyzers hints about assumed type of property")
+		for property_name, prop in pcls._properties.items():
+			ptype = self.fix_type(prop.type)
+			self.codegen.emit("assert isinstance(self.%s, %s)" % (property_name, ptype))
+		self.codegen.end()
+		self.codegen.end()
+		self.codegen.newline()
+		self.codegen.newline()
+		
+		# -- imports ---
+		import PyQt4.QtCore, PyQt4.QtGui
+		qt_modules = (PyQt4.QtCore, PyQt4.QtGui)
+		self.codegen.prepend_header("from PyQt4.QtCore import Qt, pyqtSignal")
+		for qtt in qt_types:
+			for mod in qt_modules:
+				if hasattr(mod, qtt):
+					self.codegen.prepend_header("from %s import %s" % (mod.__name__, qtt))
+					break
+		
+		
+	def fix_name(self, n):
+		if keyword.iskeyword(n):
+			return "%s_" % n
+		else:
+			return n
+
+		
+	def fix_type(self, t):
+		t = t.replace("&", "").replace("*", "").replace("::", ".")
+		if t == "QString":
+			t = "unicode"
+		return t
+
+	
+	def __str__(self):
+		return str(self.codegen)
+
+
+	
+if __name__ == "__main__":
+	from PyQt4.QtGui import QApplication, QPushButton
+	stubs = StubCreator()
+	stubs.add_class(QApplication)
+	stubs.add_class(QPushButton)
+	print stubs