7 QtCore = qt_compat.QtCore
8 QtGui = qt_compat.import_module("QtGui")
13 _moduleLogger = logging.getLogger(__name__)
16 @contextlib.contextmanager
17 def notify_error(log):
24 @contextlib.contextmanager
25 def notify_busy(log, message):
26 log.push_busy(message)
33 class ErrorMessage(object):
39 def __init__(self, message, level):
40 self._message = message
42 self._time = datetime.datetime.now()
53 return "%s.%s(%r, %r)" % (__name__, self.__class__.__name__, self._message, self._level)
56 class QErrorLog(QtCore.QObject):
58 messagePushed = qt_compat.Signal()
59 messagePopped = qt_compat.Signal()
62 QtCore.QObject.__init__(self)
65 def push_busy(self, message):
66 _moduleLogger.info("Entering state: %s" % message)
67 self._push_message(message, ErrorMessage.LEVEL_BUSY)
69 def push_message(self, message):
70 self._push_message(message, ErrorMessage.LEVEL_INFO)
72 def push_error(self, message):
73 self._push_message(message, ErrorMessage.LEVEL_ERROR)
75 def push_exception(self):
76 userMessage = str(sys.exc_info()[1])
77 _moduleLogger.exception(userMessage)
78 self.push_error(userMessage)
80 def pop(self, message = None):
84 _moduleLogger.info("Exiting state: %s" % message)
87 for (i, error) in enumerate(self._messages)
88 if error.message == message
90 # Might be removed out of order
92 del self._messages[messageIndex[0]]
93 self.messagePopped.emit()
95 def peek_message(self):
96 return self._messages[0]
98 def _push_message(self, message, level):
99 self._messages.append(ErrorMessage(message, level))
100 # Sort is defined as stable, so this should be fine
101 self._messages.sort(key=lambda x: x.level)
102 self.messagePushed.emit()
105 return len(self._messages)
108 class ErrorDisplay(object):
110 _SENTINEL_ICON = QtGui.QIcon()
112 def __init__(self, errorLog):
113 self._errorLog = errorLog
114 self._errorLog.messagePushed.connect(self._on_message_pushed)
115 self._errorLog.messagePopped.connect(self._on_message_popped)
118 self._severityLabel = QtGui.QLabel()
119 self._severityLabel.setAlignment(QtCore.Qt.AlignLeft | QtCore.Qt.AlignVCenter)
121 self._message = QtGui.QLabel()
122 self._message.setText("Boo")
123 self._message.setAlignment(QtCore.Qt.AlignLeft | QtCore.Qt.AlignVCenter)
124 self._message.setWordWrap(True)
126 self._closeLabel = None
128 self._controlLayout = QtGui.QHBoxLayout()
129 self._controlLayout.addWidget(self._severityLabel, 1, QtCore.Qt.AlignCenter)
130 self._controlLayout.addWidget(self._message, 1000)
132 self._widget = QtGui.QWidget()
133 self._widget.setLayout(self._controlLayout)
140 def _show_error(self):
141 if self._icons is None:
143 ErrorMessage.LEVEL_BUSY:
145 #("process-working", "view-refresh", "general_refresh", "gtk-refresh")
146 ("view-refresh", "general_refresh", "gtk-refresh", )
148 ErrorMessage.LEVEL_INFO:
150 ("dialog-information", "general_notes", "gtk-info")
152 ErrorMessage.LEVEL_ERROR:
154 ("dialog-error", "app_install_error", "gtk-dialog-error")
157 if self._closeLabel is None:
158 closeIcon = get_theme_icon(("window-close", "general_close", "gtk-close"), self._SENTINEL_ICON)
159 if closeIcon is not self._SENTINEL_ICON:
160 self._closeLabel = QtGui.QPushButton(closeIcon, "")
162 self._closeLabel = QtGui.QPushButton("X")
163 self._closeLabel.clicked.connect(self._on_close)
164 self._controlLayout.addWidget(self._closeLabel, 1, QtCore.Qt.AlignCenter)
165 error = self._errorLog.peek_message()
166 self._message.setText(error.message)
167 self._severityLabel.setPixmap(self._icons[error.level])
171 @qt_compat.Slot(bool)
172 @misc.log_exception(_moduleLogger)
173 def _on_close(self, checked = False):
177 @misc.log_exception(_moduleLogger)
178 def _on_message_pushed(self):
182 @misc.log_exception(_moduleLogger)
183 def _on_message_popped(self):
184 if len(self._errorLog) == 0:
185 self._message.setText("")
191 class QHtmlDelegate(QtGui.QStyledItemDelegate):
195 def __init__(self, *args, **kwd):
196 QtGui.QStyledItemDelegate.__init__(*((self, ) + args), **kwd)
197 self._width = self.UNDEFINED_SIZE
199 def paint(self, painter, option, index):
200 newOption = QtGui.QStyleOptionViewItemV4(option)
201 self.initStyleOption(newOption, index)
202 if newOption.widget is not None:
203 style = newOption.widget.style()
205 style = QtGui.QApplication.style()
207 doc = QtGui.QTextDocument()
208 doc.setHtml(newOption.text)
209 doc.setTextWidth(newOption.rect.width())
212 style.drawControl(QtGui.QStyle.CE_ItemViewItem, newOption, painter)
214 ctx = QtGui.QAbstractTextDocumentLayout.PaintContext()
215 if newOption.state & QtGui.QStyle.State_Selected:
216 ctx.palette.setColor(
218 newOption.palette.color(
219 QtGui.QPalette.Active,
220 QtGui.QPalette.HighlightedText
224 ctx.palette.setColor(
226 newOption.palette.color(
227 QtGui.QPalette.Active,
232 textRect = style.subElementRect(QtGui.QStyle.SE_ItemViewItemText, newOption)
234 painter.translate(textRect.topLeft())
235 painter.setClipRect(textRect.translated(-textRect.topLeft()))
236 doc.documentLayout().draw(painter, ctx)
239 def setWidth(self, width, model):
240 if self._width == width:
243 for c in xrange(model.rowCount()):
244 cItem = model.item(c, 0)
245 for r in xrange(model.rowCount()):
246 rItem = cItem.child(r, 0)
247 rIndex = model.indexFromItem(rItem)
248 self.sizeHintChanged.emit(rIndex)
251 def sizeHint(self, option, index):
252 newOption = QtGui.QStyleOptionViewItemV4(option)
253 self.initStyleOption(newOption, index)
255 doc = QtGui.QTextDocument()
256 doc.setHtml(newOption.text)
257 if self._width != self.UNDEFINED_SIZE:
260 width = newOption.rect.width()
261 doc.setTextWidth(width)
262 size = QtCore.QSize(doc.idealWidth(), doc.size().height())
266 class QSignalingMainWindow(QtGui.QMainWindow):
268 closed = qt_compat.Signal()
269 hidden = qt_compat.Signal()
270 shown = qt_compat.Signal()
271 resized = qt_compat.Signal()
273 def __init__(self, *args, **kwd):
274 QtGui.QMainWindow.__init__(*((self, )+args), **kwd)
276 def closeEvent(self, event):
277 val = QtGui.QMainWindow.closeEvent(self, event)
281 def hideEvent(self, event):
282 val = QtGui.QMainWindow.hideEvent(self, event)
286 def showEvent(self, event):
287 val = QtGui.QMainWindow.showEvent(self, event)
291 def resizeEvent(self, event):
292 val = QtGui.QMainWindow.resizeEvent(self, event)
296 def set_current_index(selector, itemText, default = 0):
297 for i in xrange(selector.count()):
298 if selector.itemText(i) == itemText:
299 selector.setCurrentIndex(i)
302 itemText.setCurrentIndex(default)
305 def _null_set_stackable(window, isStackable):
309 def _maemo_set_stackable(window, isStackable):
310 window.setAttribute(QtCore.Qt.WA_Maemo5StackedWindow, isStackable)
314 QtCore.Qt.WA_Maemo5StackedWindow
315 set_stackable = _maemo_set_stackable
316 except AttributeError:
317 set_stackable = _null_set_stackable
320 def _null_set_autorient(window, doAutoOrient):
324 def _maemo_set_autorient(window, doAutoOrient):
325 window.setAttribute(QtCore.Qt.WA_Maemo5AutoOrientation, doAutoOrient)
329 QtCore.Qt.WA_Maemo5AutoOrientation
330 set_autorient = _maemo_set_autorient
331 except AttributeError:
332 set_autorient = _null_set_autorient
335 def screen_orientation():
336 geom = QtGui.QApplication.desktop().screenGeometry()
337 if geom.width() <= geom.height():
338 return QtCore.Qt.Vertical
340 return QtCore.Qt.Horizontal
343 def _null_set_window_orientation(window, orientation):
347 def _maemo_set_window_orientation(window, orientation):
348 if orientation == QtCore.Qt.Vertical:
349 window.setAttribute(QtCore.Qt.WA_Maemo5LandscapeOrientation, False)
350 window.setAttribute(QtCore.Qt.WA_Maemo5PortraitOrientation, True)
351 elif orientation == QtCore.Qt.Horizontal:
352 window.setAttribute(QtCore.Qt.WA_Maemo5LandscapeOrientation, True)
353 window.setAttribute(QtCore.Qt.WA_Maemo5PortraitOrientation, False)
354 elif orientation is None:
355 window.setAttribute(QtCore.Qt.WA_Maemo5LandscapeOrientation, False)
356 window.setAttribute(QtCore.Qt.WA_Maemo5PortraitOrientation, False)
358 raise RuntimeError("Unknown orientation: %r" % orientation)
362 QtCore.Qt.WA_Maemo5LandscapeOrientation
363 QtCore.Qt.WA_Maemo5PortraitOrientation
364 set_window_orientation = _maemo_set_window_orientation
365 except AttributeError:
366 set_window_orientation = _null_set_window_orientation
369 def _null_show_progress_indicator(window, isStackable):
373 def _maemo_show_progress_indicator(window, isStackable):
374 window.setAttribute(QtCore.Qt.WA_Maemo5ShowProgressIndicator, isStackable)
378 QtCore.Qt.WA_Maemo5ShowProgressIndicator
379 show_progress_indicator = _maemo_show_progress_indicator
380 except AttributeError:
381 show_progress_indicator = _null_show_progress_indicator
384 def _null_mark_numbers_preferred(widget):
388 def _newqt_mark_numbers_preferred(widget):
389 widget.setInputMethodHints(QtCore.Qt.ImhPreferNumbers)
393 QtCore.Qt.ImhPreferNumbers
394 mark_numbers_preferred = _newqt_mark_numbers_preferred
395 except AttributeError:
396 mark_numbers_preferred = _null_mark_numbers_preferred
399 def _null_get_theme_icon(iconNames, fallback = None):
400 icon = fallback if fallback is not None else QtGui.QIcon()
404 def _newqt_get_theme_icon(iconNames, fallback = None):
405 for iconName in iconNames:
406 if QtGui.QIcon.hasThemeIcon(iconName):
407 icon = QtGui.QIcon.fromTheme(iconName)
410 icon = fallback if fallback is not None else QtGui.QIcon()
415 QtGui.QIcon.fromTheme
416 get_theme_icon = _newqt_get_theme_icon
417 except AttributeError:
418 get_theme_icon = _null_get_theme_icon