7 QtCore = qt_compat.QtCore
12 _moduleLogger = logging.getLogger(__name__)
15 class QThread44(QtCore.QThread):
17 This is to imitate QThread in Qt 4.4+ for when running on older version
18 See http://labs.trolltech.com/blogs/2010/06/17/youre-doing-it-wrong
19 (On Lucid I have Qt 4.7 and this is still an issue)
22 def __init__(self, parent = None):
23 QtCore.QThread.__init__(self, parent)
29 class _WorkerThread(QtCore.QObject):
31 _taskComplete = qt_compat.Signal(object)
33 def __init__(self, futureThread):
34 QtCore.QObject.__init__(self)
35 self._futureThread = futureThread
36 self._futureThread._addTask.connect(self._on_task_added)
37 self._taskComplete.connect(self._futureThread._on_task_complete)
39 @qt_compat.Slot(object)
40 def _on_task_added(self, task):
41 self.__on_task_added(task)
43 @misc.log_exception(_moduleLogger)
44 def __on_task_added(self, task):
45 if not self._futureThread._isRunning:
46 _moduleLogger.error("Dropping task")
48 func, args, kwds, on_success, on_error = task
51 result = func(*args, **kwds)
54 _moduleLogger.error("Error, passing it back to the main thread")
58 taskResult = on_success, on_error, isError, result
59 self._taskComplete.emit(taskResult)
62 class FutureThread(QtCore.QObject):
64 _addTask = qt_compat.Signal(object)
67 QtCore.QObject.__init__(self)
68 self._thread = QThread44()
69 self._isRunning = False
70 self._worker = _WorkerThread(self)
71 self._worker.moveToThread(self._thread)
75 self._isRunning = True
78 self._isRunning = False
81 def add_task(self, func, args, kwds, on_success, on_error):
82 assert self._isRunning, "Task queue not started"
83 task = func, args, kwds, on_success, on_error
84 self._addTask.emit(task)
86 @qt_compat.Slot(object)
87 def _on_task_complete(self, taskResult):
88 self.__on_task_complete(taskResult)
90 @misc.log_exception(_moduleLogger)
91 def __on_task_complete(self, taskResult):
92 on_success, on_error, isError, result = taskResult
93 if not self._isRunning:
95 _moduleLogger.error("Masking: %s" % (result, ))
97 result = StopIteration("Cancelling all callbacks")
98 callback = on_success if not isError else on_error
102 _moduleLogger.exception("Callback errored")
105 def create_single_column_list_model(columnName, **kwargs):
107 >>> class Single(object): pass
108 >>> SingleListModel = create_single_column_list_model("s")
109 >>> slm = SingleListModel([Single(), Single(), Single()])
112 class SingleColumnListModel(QtCore.QAbstractListModel):
114 def __init__(self, l = None):
115 QtCore.QAbstractListModel.__init__(self)
116 self._list = l if l is not None else []
117 self.setRoleNames({0: columnName})
120 return len(self._list)
122 def __getitem__(self, key):
123 return self._list[key]
125 def __setitem__(self, key, value):
126 with scoped_model_reset(self):
127 self._list[key] = value
129 def __delitem__(self, key):
130 with scoped_model_reset(self):
134 return iter(self._list)
137 return '<%s (%s)>' % (
138 self.__class__.__name__,
142 def rowCount(self, parent=QtCore.QModelIndex()):
143 return len(self._list)
145 def data(self, index, role):
146 if index.isValid() and role == 0:
147 return self._list[index.row()]
151 SingleColumnListModel.__name__ = kwargs["name"]
153 return SingleColumnListModel
156 def create_tupled_list_model(*columnNames, **kwargs):
158 >>> class Column0(object): pass
159 >>> class Column1(object): pass
160 >>> class Column2(object): pass
161 >>> MultiColumnedListModel = create_tupled_list_model("c0", "c1", "c2")
162 >>> mclm = MultiColumnedListModel([(Column0(), Column1(), Column2())])
165 class TupledListModel(QtCore.QAbstractListModel):
167 def __init__(self, l = None):
168 QtCore.QAbstractListModel.__init__(self)
169 self._list = l if l is not None else []
170 self.setRoleNames(dict(enumerate(columnNames)))
173 return len(self._list)
175 def __getitem__(self, key):
176 return self._list[key]
178 def __setitem__(self, key, value):
179 with scoped_model_reset(self):
180 self._list[key] = value
182 def __delitem__(self, key):
183 with scoped_model_reset(self):
187 return iter(self._list)
190 return '<%s (%s)>' % (
191 self.__class__.__name__,
192 ', '.join(columnNames),
195 def rowCount(self, parent=QtCore.QModelIndex()):
196 return len(self._list)
198 def data(self, index, role):
199 if index.isValid() and 0 <= role and role < len(columnNames):
200 return self._list[index.row()][role]
204 TupledListModel.__name__ = kwargs["name"]
206 return TupledListModel
209 class FileSystemModel(QtCore.QAbstractListModel):
211 Wrapper around QtGui.QFileSystemModel
226 ALLINFOS = FILEINFOS + EXTINFOS
228 def __init__(self, model, path):
229 QtCore.QAbstractListModel.__init__(self)
233 self._rootIndex = self._model.index(self._path)
236 self.setRoleNames(dict(enumerate(self.ALLINFOS)))
237 self._model.directoryLoaded.connect(self._on_directory_loaded)
239 childChanged = qt_compat.Signal(QtCore.QObject)
242 assert self._child is not None
245 child = qt_compat.Property(QtCore.QObject, _child, notify=childChanged)
247 backendChanged = qt_compat.Signal()
250 finfo = self._model.fileInfo(self._rootIndex)
251 return finfo.fileName()
253 parent = qt_compat.Property(str, _parent, notify=backendChanged)
256 def browse_to(self, path):
257 if self._child is None:
258 self._child = FileSystemModel(self._model, path)
260 self._child.switch_to(path)
261 self.childChanged.emit()
265 def switch_to(self, path):
266 with scoped_model_reset(self):
268 self._rootIndex = self._model.index(self._path)
269 self.backendChanged.emit()
272 return self._model.rowCount(self._rootIndex)
274 def __getitem__(self, key):
275 return self._model.index(key, 0, self._rootIndex)
278 return (self[i] for i in xrange(len(self)))
280 def rowCount(self, parent=QtCore.QModelIndex()):
283 def data(self, index, role):
284 if index.isValid() and 0 <= role and role < len(self.ALLINFOS):
285 internalIndex = self._translate_index(index)
286 info = self._model.fileInfo(internalIndex)
287 if role < len(self.FILEINFOS):
288 field = self.FILEINFOS[role]
289 value = getattr(info, field)()
291 role -= len(self.FILEINFOS)
292 field = self.EXTINFOS[role]
294 return self._model.type(internalIndex)
296 raise NotImplementedError("Out of range that was already checked")
300 def _on_directory_loaded(self, path):
301 if self._path == path:
302 self.backendChanged.emit()
305 def _translate_index(self, externalIndex):
306 internalIndex = self._model.index(externalIndex.row(), 0, self._rootIndex)
310 @contextlib.contextmanager
311 def scoped_model_reset(model):
312 model.beginResetModel()
316 model.endResetModel()
319 def create_qobject(*classDef, **kwargs):
321 >>> Car = create_qobject(
325 ... ('inStock', bool),
329 <class '__main__.AutoQObject'>
331 >>> c = Car(model='Fiesta', brand='Ford', year=1337)
332 >>> print c.model, c.brand, c.year, c.inStock
333 Fiesta Ford 1337 False
335 <Car (model='Fiesta', brand='Ford', year=1337, inStock=False)>
339 >>> print c.model, c.brand, c.year, c.inStock
340 Fiesta Ford 1337 True
342 <Car (model='Fiesta', brand='Ford', year=1337, inStock=True)>
345 class AutoQObject(QtCore.QObject):
347 def __init__(self, **initKwargs):
348 QtCore.QObject.__init__(self)
349 for key, val in classDef:
350 setattr(self, '_'+key, initKwargs.get(key, val()))
354 '%s=%r' % (key, getattr(self, '_'+key))
355 for key, value in classDef
357 return '<%s (%s)>' % (
358 kwargs.get('name', self.__class__.__name__),
362 for key, value in classDef:
363 nfy = locals()['_nfy_'+key] = qt_compat.Signal()
367 return self.__dict__['_'+key]
372 setattr(self, '_'+key, value)
373 getattr(self, '_nfy_'+key).emit()
376 setter = locals()['_set_'+key] = _set(key)
377 getter = locals()['_get_'+key] = _get(key)
379 locals()[key] = qt_compat.Property(value, getter, setter, notify=nfy)
380 del nfy, _get, _set, getter, setter
385 class QObjectProxy(object):
387 Proxy for accessing properties and slots as attributes
389 This class acts as a proxy for the object for which it is
390 created, and makes property access more Pythonic while
391 still allowing access to slots (as member functions).
393 Attribute names starting with '_' are not proxied.
396 def __init__(self, rootObject):
397 self._rootObject = rootObject
398 m = self._rootObject.metaObject()
401 for i in xrange(m.propertyCount())
404 def __getattr__(self, key):
405 value = self._rootObject.property(key)
407 # No such property, so assume we call a slot
408 if value is None and key not in self._properties:
409 return getattr(self._rootObject, key)
413 def __setattr__(self, key, value):
414 if key.startswith('_'):
415 object.__setattr__(self, key, value)
417 self._rootObject.setProperty(key, value)
420 if __name__ == "__main__":
422 print doctest.testmod()