3 from __future__ import with_statement
9 QtCore = qt_compat.QtCore
14 _moduleLogger = logging.getLogger(__name__)
17 class QThread44(QtCore.QThread):
19 This is to imitate QThread in Qt 4.4+ for when running on older version
20 See http://labs.trolltech.com/blogs/2010/06/17/youre-doing-it-wrong
21 (On Lucid I have Qt 4.7 and this is still an issue)
24 def __init__(self, parent = None):
25 QtCore.QThread.__init__(self, parent)
31 class _WorkerThread(QtCore.QObject):
33 _taskComplete = qt_compat.Signal(object)
35 def __init__(self, futureThread):
36 QtCore.QObject.__init__(self)
37 self._futureThread = futureThread
38 self._futureThread._addTask.connect(self._on_task_added)
39 self._taskComplete.connect(self._futureThread._on_task_complete)
41 @qt_compat.Slot(object)
42 def _on_task_added(self, task):
43 self.__on_task_added(task)
45 @misc.log_exception(_moduleLogger)
46 def __on_task_added(self, task):
47 if not self._futureThread._isRunning:
48 _moduleLogger.error("Dropping task")
50 func, args, kwds, on_success, on_error = task
53 result = func(*args, **kwds)
56 _moduleLogger.error("Error, passing it back to the main thread")
60 taskResult = on_success, on_error, isError, result
61 self._taskComplete.emit(taskResult)
64 class FutureThread(QtCore.QObject):
66 _addTask = qt_compat.Signal(object)
69 QtCore.QObject.__init__(self)
70 self._thread = QThread44()
71 self._isRunning = False
72 self._worker = _WorkerThread(self)
73 self._worker.moveToThread(self._thread)
77 self._isRunning = True
80 self._isRunning = False
83 def add_task(self, func, args, kwds, on_success, on_error):
84 assert self._isRunning, "Task queue not started"
85 task = func, args, kwds, on_success, on_error
86 self._addTask.emit(task)
88 @qt_compat.Slot(object)
89 def _on_task_complete(self, taskResult):
90 self.__on_task_complete(taskResult)
92 @misc.log_exception(_moduleLogger)
93 def __on_task_complete(self, taskResult):
94 on_success, on_error, isError, result = taskResult
95 if not self._isRunning:
97 _moduleLogger.error("Masking: %s" % (result, ))
99 result = StopIteration("Cancelling all callbacks")
100 callback = on_success if not isError else on_error
104 _moduleLogger.exception("Callback errored")
107 def create_single_column_list_model(columnName, **kwargs):
109 >>> class Single(object): pass
110 >>> SingleListModel = create_single_column_list_model("s")
111 >>> slm = SingleListModel([Single(), Single(), Single()])
114 class SingleColumnListModel(QtCore.QAbstractListModel):
116 def __init__(self, l = None):
117 QtCore.QAbstractListModel.__init__(self)
118 self._list = l if l is not None else []
119 self.setRoleNames({0: columnName})
122 return len(self._list)
124 def __getitem__(self, key):
125 return self._list[key]
127 def __setitem__(self, key, value):
128 with scoped_model_reset(self):
129 self._list[key] = value
131 def __delitem__(self, key):
132 with scoped_model_reset(self):
136 return iter(self._list)
139 return '<%s (%s)>' % (
140 self.__class__.__name__,
144 def rowCount(self, parent=QtCore.QModelIndex()):
145 return len(self._list)
147 def data(self, index, role):
148 if index.isValid() and role == 0:
149 return self._list[index.row()]
153 SingleColumnListModel.__name__ = kwargs["name"]
155 return SingleColumnListModel
158 def create_tupled_list_model(*columnNames, **kwargs):
160 >>> class Column0(object): pass
161 >>> class Column1(object): pass
162 >>> class Column2(object): pass
163 >>> MultiColumnedListModel = create_tupled_list_model("c0", "c1", "c2")
164 >>> mclm = MultiColumnedListModel([(Column0(), Column1(), Column2())])
167 class TupledListModel(QtCore.QAbstractListModel):
169 def __init__(self, l = None):
170 QtCore.QAbstractListModel.__init__(self)
171 self._list = l if l is not None else []
172 self.setRoleNames(dict(enumerate(columnNames)))
175 return len(self._list)
177 def __getitem__(self, key):
178 return self._list[key]
180 def __setitem__(self, key, value):
181 with scoped_model_reset(self):
182 self._list[key] = value
184 def __delitem__(self, key):
185 with scoped_model_reset(self):
189 return iter(self._list)
192 return '<%s (%s)>' % (
193 self.__class__.__name__,
194 ', '.join(columnNames),
197 def rowCount(self, parent=QtCore.QModelIndex()):
198 return len(self._list)
200 def data(self, index, role):
201 if index.isValid() and 0 <= role and role < len(columnNames):
202 return self._list[index.row()][role]
206 TupledListModel.__name__ = kwargs["name"]
208 return TupledListModel
211 class FileSystemModel(QtCore.QAbstractListModel):
213 Wrapper around QtGui.QFileSystemModel
228 ALLINFOS = FILEINFOS + EXTINFOS
230 def __init__(self, model, path):
231 QtCore.QAbstractListModel.__init__(self)
235 self._rootIndex = self._model.index(self._path)
238 self.setRoleNames(dict(enumerate(self.ALLINFOS)))
239 self._model.directoryLoaded.connect(self._on_directory_loaded)
241 childChanged = qt_compat.Signal(QtCore.QObject)
244 assert self._child is not None
247 child = qt_compat.Property(QtCore.QObject, _child, notify=childChanged)
249 backendChanged = qt_compat.Signal()
252 finfo = self._model.fileInfo(self._rootIndex)
253 return finfo.fileName()
255 parent = qt_compat.Property(str, _parent, notify=backendChanged)
258 def browse_to(self, path):
259 if self._child is None:
260 self._child = FileSystemModel(self._model, path)
262 self._child.switch_to(path)
263 self.childChanged.emit()
267 def switch_to(self, path):
268 with scoped_model_reset(self):
270 self._rootIndex = self._model.index(self._path)
271 self.backendChanged.emit()
274 return self._model.rowCount(self._rootIndex)
276 def __getitem__(self, key):
277 return self._model.index(key, 0, self._rootIndex)
280 return (self[i] for i in xrange(len(self)))
282 def rowCount(self, parent=QtCore.QModelIndex()):
285 def data(self, index, role):
286 if index.isValid() and 0 <= role and role < len(self.ALLINFOS):
287 internalIndex = self._translate_index(index)
288 info = self._model.fileInfo(internalIndex)
289 if role < len(self.FILEINFOS):
290 field = self.FILEINFOS[role]
291 value = getattr(info, field)()
293 role -= len(self.FILEINFOS)
294 field = self.EXTINFOS[role]
296 return self._model.type(internalIndex)
298 raise NotImplementedError("Out of range that was already checked")
302 def _on_directory_loaded(self, path):
303 if self._path == path:
304 self.backendChanged.emit()
307 def _translate_index(self, externalIndex):
308 internalIndex = self._model.index(externalIndex.row(), 0, self._rootIndex)
312 @contextlib.contextmanager
313 def scoped_model_reset(model):
314 model.beginResetModel()
318 model.endResetModel()
321 def create_qobject(*classDef, **kwargs):
323 >>> Car = create_qobject(
327 ... ('inStock', bool),
331 <class '__main__.AutoQObject'>
333 >>> c = Car(model='Fiesta', brand='Ford', year=1337)
334 >>> print c.model, c.brand, c.year, c.inStock
335 Fiesta Ford 1337 False
337 <Car (model='Fiesta', brand='Ford', year=1337, inStock=False)>
341 >>> print c.model, c.brand, c.year, c.inStock
342 Fiesta Ford 1337 True
344 <Car (model='Fiesta', brand='Ford', year=1337, inStock=True)>
347 class AutoQObject(QtCore.QObject):
349 def __init__(self, **initKwargs):
350 QtCore.QObject.__init__(self)
351 for key, val in classDef:
352 setattr(self, '_'+key, initKwargs.get(key, val()))
356 '%s=%r' % (key, getattr(self, '_'+key))
357 for key, value in classDef
359 return '<%s (%s)>' % (
360 kwargs.get('name', self.__class__.__name__),
364 for key, value in classDef:
365 nfy = locals()['_nfy_'+key] = qt_compat.Signal()
369 return self.__dict__['_'+key]
374 setattr(self, '_'+key, value)
375 getattr(self, '_nfy_'+key).emit()
378 setter = locals()['_set_'+key] = _set(key)
379 getter = locals()['_get_'+key] = _get(key)
381 locals()[key] = qt_compat.Property(value, getter, setter, notify=nfy)
382 del nfy, _get, _set, getter, setter
387 class QObjectProxy(object):
389 Proxy for accessing properties and slots as attributes
391 This class acts as a proxy for the object for which it is
392 created, and makes property access more Pythonic while
393 still allowing access to slots (as member functions).
395 Attribute names starting with '_' are not proxied.
398 def __init__(self, rootObject):
399 self._rootObject = rootObject
400 m = self._rootObject.metaObject()
403 for i in xrange(m.propertyCount())
406 def __getattr__(self, key):
407 value = self._rootObject.property(key)
409 # No such property, so assume we call a slot
410 if value is None and key not in self._properties:
411 return getattr(self._rootObject, key)
415 def __setattr__(self, key, value):
416 if key.startswith('_'):
417 object.__setattr__(self, key, value)
419 self._rootObject.setProperty(key, value)
422 if __name__ == "__main__":
424 print doctest.testmod()