First attempt at distutils support now that the locations have been updated
[gc-dialer] / dialcentral / util / qore_utils.py
index 153558d..9e85c58 100644 (file)
@@ -1,3 +1,6 @@
+#!/usr/bin/env python
+
+import contextlib
 import logging
 
 import qt_compat
@@ -97,3 +100,323 @@ class FutureThread(QtCore.QObject):
                        callback(result)
                except Exception:
                        _moduleLogger.exception("Callback errored")
+
+
+def create_single_column_list_model(columnName, **kwargs):
+       """
+       >>> class Single(object): pass
+       >>> SingleListModel = create_single_column_list_model("s")
+       >>> slm = SingleListModel([Single(), Single(), Single()])
+       """
+
+       class SingleColumnListModel(QtCore.QAbstractListModel):
+
+               def __init__(self, l = None):
+                       QtCore.QAbstractListModel.__init__(self)
+                       self._list = l if l is not None else []
+                       self.setRoleNames({0: columnName})
+
+               def __len__(self):
+                       return len(self._list)
+
+               def __getitem__(self, key):
+                       return self._list[key]
+
+               def __setitem__(self, key, value):
+                       with scoped_model_reset(self):
+                               self._list[key] = value
+
+               def __delitem__(self, key):
+                       with scoped_model_reset(self):
+                               del self._list[key]
+
+               def __iter__(self):
+                       return iter(self._list)
+
+               def __repr__(self):
+                       return '<%s (%s)>' % (
+                               self.__class__.__name__,
+                               columnName,
+                       )
+
+               def rowCount(self, parent=QtCore.QModelIndex()):
+                       return len(self._list)
+
+               def data(self, index, role):
+                       if index.isValid() and role == 0:
+                               return self._list[index.row()]
+                       return None
+
+       if "name" in kwargs:
+               SingleColumnListModel.__name__ = kwargs["name"]
+
+       return SingleColumnListModel
+
+
+def create_tupled_list_model(*columnNames, **kwargs):
+       """
+       >>> class Column0(object): pass
+       >>> class Column1(object): pass
+       >>> class Column2(object): pass
+       >>> MultiColumnedListModel = create_tupled_list_model("c0", "c1", "c2")
+       >>> mclm = MultiColumnedListModel([(Column0(), Column1(), Column2())])
+       """
+
+       class TupledListModel(QtCore.QAbstractListModel):
+
+               def __init__(self, l = None):
+                       QtCore.QAbstractListModel.__init__(self)
+                       self._list = l if l is not None else []
+                       self.setRoleNames(dict(enumerate(columnNames)))
+
+               def __len__(self):
+                       return len(self._list)
+
+               def __getitem__(self, key):
+                       return self._list[key]
+
+               def __setitem__(self, key, value):
+                       with scoped_model_reset(self):
+                               self._list[key] = value
+
+               def __delitem__(self, key):
+                       with scoped_model_reset(self):
+                               del self._list[key]
+
+               def __iter__(self):
+                       return iter(self._list)
+
+               def __repr__(self):
+                       return '<%s (%s)>' % (
+                               self.__class__.__name__,
+                               ', '.join(columnNames),
+                       )
+
+               def rowCount(self, parent=QtCore.QModelIndex()):
+                       return len(self._list)
+
+               def data(self, index, role):
+                       if index.isValid() and 0 <= role and role < len(columnNames):
+                               return self._list[index.row()][role]
+                       return None
+
+       if "name" in kwargs:
+               TupledListModel.__name__ = kwargs["name"]
+
+       return TupledListModel
+
+
+class FileSystemModel(QtCore.QAbstractListModel):
+       """
+       Wrapper around QtGui.QFileSystemModel
+       """
+
+       FILEINFOS = [
+               "fileName",
+               "isDir",
+               "filePath",
+               "completeSuffix",
+               "baseName",
+       ]
+
+       EXTINFOS = [
+               "type",
+       ]
+
+       ALLINFOS = FILEINFOS + EXTINFOS
+
+       def __init__(self, model, path):
+               QtCore.QAbstractListModel.__init__(self)
+               self._path = path
+
+               self._model = model
+               self._rootIndex = self._model.index(self._path)
+
+               self._child = None
+               self.setRoleNames(dict(enumerate(self.ALLINFOS)))
+               self._model.directoryLoaded.connect(self._on_directory_loaded)
+
+       childChanged = qt_compat.Signal(QtCore.QObject)
+
+       def _child(self):
+               assert self._child is not None
+               return self._child
+
+       child = qt_compat.Property(QtCore.QObject, _child, notify=childChanged)
+
+       backendChanged = qt_compat.Signal()
+
+       def _parent(self):
+               finfo = self._model.fileInfo(self._rootIndex)
+               return finfo.fileName()
+
+       parent = qt_compat.Property(str, _parent, notify=backendChanged)
+
+       @qt_compat.Slot(str)
+       def browse_to(self, path):
+               if self._child is None:
+                       self._child = FileSystemModel(self._model, path)
+               else:
+                       self._child.switch_to(path)
+               self.childChanged.emit()
+               return self._child
+
+       @qt_compat.Slot(str)
+       def switch_to(self, path):
+               with scoped_model_reset(self):
+                       self._path = path
+                       self._rootIndex = self._model.index(self._path)
+               self.backendChanged.emit()
+
+       def __len__(self):
+               return self._model.rowCount(self._rootIndex)
+
+       def __getitem__(self, key):
+               return self._model.index(key, 0, self._rootIndex)
+
+       def __iter__(self):
+               return (self[i] for i in xrange(len(self)))
+
+       def rowCount(self, parent=QtCore.QModelIndex()):
+               return len(self)
+
+       def data(self, index, role):
+               if index.isValid() and 0 <= role and role < len(self.ALLINFOS):
+                       internalIndex = self._translate_index(index)
+                       info = self._model.fileInfo(internalIndex)
+                       if role < len(self.FILEINFOS):
+                               field = self.FILEINFOS[role]
+                               value = getattr(info, field)()
+                       else:
+                               role -= len(self.FILEINFOS)
+                               field = self.EXTINFOS[role]
+                               if field == "type":
+                                       return self._model.type(internalIndex)
+                               else:
+                                       raise NotImplementedError("Out of range that was already checked")
+                       return value
+               return None
+
+       def _on_directory_loaded(self, path):
+               if self._path == path:
+                       self.backendChanged.emit()
+                       self.reset()
+
+       def _translate_index(self, externalIndex):
+               internalIndex = self._model.index(externalIndex.row(), 0, self._rootIndex)
+               return internalIndex
+
+
+@contextlib.contextmanager
+def scoped_model_reset(model):
+       model.beginResetModel()
+       try:
+               yield
+       finally:
+               model.endResetModel()
+
+
+def create_qobject(*classDef, **kwargs):
+       """
+       >>> Car = create_qobject(
+       ...     ('model', str),
+       ...     ('brand', str),
+       ...     ('year', int),
+       ...     ('inStock', bool),
+       ...     name='Car'
+       ... )
+       >>> print Car
+       <class '__main__.AutoQObject'>
+       >>>  
+       >>> c = Car(model='Fiesta', brand='Ford', year=1337)
+       >>> print c.model, c.brand, c.year, c.inStock
+       Fiesta Ford 1337 False
+       >>> print c
+       <Car (model='Fiesta', brand='Ford', year=1337, inStock=False)>
+       >>>  
+       >>> c.inStock = True
+       >>>  
+       >>> print c.model, c.brand, c.year, c.inStock
+       Fiesta Ford 1337 True
+       >>> print c
+       <Car (model='Fiesta', brand='Ford', year=1337, inStock=True)>
+       """
+
+       class AutoQObject(QtCore.QObject):
+
+               def __init__(self, **initKwargs):
+                       QtCore.QObject.__init__(self)
+                       for key, val in classDef:
+                               setattr(self, '_'+key, initKwargs.get(key, val()))
+
+               def __repr__(self):
+                       values = (
+                               '%s=%r' % (key, getattr(self, '_'+key))
+                               for key, value in classDef
+                       )
+                       return '<%s (%s)>' % (
+                               kwargs.get('name', self.__class__.__name__),
+                               ', '.join(values),
+                       )
+
+               for key, value in classDef:
+                       nfy = locals()['_nfy_'+key] = qt_compat.Signal()
+
+                       def _get(key):
+                               def f(self):
+                                       return self.__dict__['_'+key]
+                               return f
+
+                       def _set(key):
+                               def f(self, value):
+                                       setattr(self, '_'+key, value)
+                                       getattr(self, '_nfy_'+key).emit()
+                               return f
+
+                       setter = locals()['_set_'+key] = _set(key)
+                       getter = locals()['_get_'+key] = _get(key)
+
+                       locals()[key] = qt_compat.Property(value, getter, setter, notify=nfy)
+               del nfy, _get, _set, getter, setter
+
+       return AutoQObject
+
+
+class QObjectProxy(object):
+       """
+       Proxy for accessing properties and slots as attributes
+
+       This class acts as a proxy for the object for which it is
+       created, and makes property access more Pythonic while
+       still allowing access to slots (as member functions).
+
+       Attribute names starting with '_' are not proxied.
+       """
+
+       def __init__(self, rootObject):
+               self._rootObject = rootObject
+               m = self._rootObject.metaObject()
+               self._properties = [
+                       m.property(i).name()
+                       for i in xrange(m.propertyCount())
+               ]
+
+       def __getattr__(self, key):
+               value = self._rootObject.property(key)
+
+               # No such property, so assume we call a slot
+               if value is None and key not in self._properties:
+                       return getattr(self._rootObject, key)
+
+               return value
+
+       def __setattr__(self, key, value):
+               if key.startswith('_'):
+                       object.__setattr__(self, key, value)
+               else:
+                       self._rootObject.setProperty(key, value)
+
+
+if __name__ == "__main__":
+       import doctest
+       print doctest.testmod()