5 from PyQt4 import QtCore
6 from PyQt4 import QtGui
11 _moduleLogger = logging.getLogger(__name__)
14 @contextlib.contextmanager
15 def notify_error(log):
22 class ErrorMessage(object):
28 def __init__(self, message, level):
29 self._message = message
41 class QErrorLog(QtCore.QObject):
43 messagePushed = QtCore.pyqtSignal()
44 messagePopped = QtCore.pyqtSignal()
47 QtCore.QObject.__init__(self)
50 def push_busy(self, message):
51 _moduleLogger.info("Entering state: %s" % message)
52 self._push_message(message, ErrorMessage.LEVEL_BUSY)
54 def push_message(self, message):
55 self._push_message(message, ErrorMessage.LEVEL_INFO)
57 def push_error(self, message):
58 self._push_message(message, ErrorMessage.LEVEL_ERROR)
60 def push_exception(self):
61 userMessage = str(sys.exc_info()[1])
62 _moduleLogger.exception(userMessage)
63 self.push_error(userMessage)
65 def pop(self, message = None):
69 _moduleLogger.info("Exiting state: %s" % message)
72 for (i, error) in enumerate(self._messages)
73 if error.message == message
75 # Might be removed out of order
77 del self._messages[messageIndex[0]]
78 self.messagePopped.emit()
80 def peek_message(self):
81 return self._messages[0]
83 def _push_message(self, message, level):
84 self._messages.append(ErrorMessage(message, level))
85 self.messagePushed.emit()
88 return len(self._messages)
91 class ErrorDisplay(object):
93 _SENTINEL_ICON = QtGui.QIcon()
95 def __init__(self, errorLog):
96 self._errorLog = errorLog
97 self._errorLog.messagePushed.connect(self._on_message_pushed)
98 self._errorLog.messagePopped.connect(self._on_message_popped)
101 ErrorMessage.LEVEL_BUSY:
103 #("process-working", "gtk-refresh")
106 ErrorMessage.LEVEL_INFO:
108 ("dialog-information", "general_notes", "gtk-info")
110 ErrorMessage.LEVEL_ERROR:
112 ("dialog-error", "app_install_error", "gtk-dialog-error")
115 self._severityLabel = QtGui.QLabel()
116 self._severityLabel.setAlignment(QtCore.Qt.AlignLeft | QtCore.Qt.AlignVCenter)
118 self._message = QtGui.QLabel()
119 self._message.setText("Boo")
120 self._message.setAlignment(QtCore.Qt.AlignLeft | QtCore.Qt.AlignVCenter)
121 self._message.setWordWrap(True)
123 closeIcon = get_theme_icon(("window-close", "general_close", "gtk-close"), self._SENTINEL_ICON)
124 if closeIcon is not self._SENTINEL_ICON:
125 self._closeLabel = QtGui.QPushButton(closeIcon, "")
127 self._closeLabel = QtGui.QPushButton("X")
128 self._closeLabel.clicked.connect(self._on_close)
130 self._controlLayout = QtGui.QHBoxLayout()
131 self._controlLayout.addWidget(self._severityLabel, 1, QtCore.Qt.AlignCenter)
132 self._controlLayout.addWidget(self._message, 1000)
133 self._controlLayout.addWidget(self._closeLabel, 1, QtCore.Qt.AlignCenter)
135 self._topLevelLayout = QtGui.QHBoxLayout()
136 self._topLevelLayout.addLayout(self._controlLayout)
137 self._widget = QtGui.QWidget()
138 self._widget.setLayout(self._topLevelLayout)
146 @QtCore.pyqtSlot(bool)
147 @misc.log_exception(_moduleLogger)
148 def _on_close(self, checked = False):
152 @misc.log_exception(_moduleLogger)
153 def _on_message_pushed(self):
154 if 1 <= len(self._errorLog) and self._widget.isHidden():
155 error = self._errorLog.peek_message()
156 self._message.setText(error.message)
157 self._severityLabel.setPixmap(self._icons[error.level])
161 @misc.log_exception(_moduleLogger)
162 def _on_message_popped(self):
163 if len(self._errorLog) == 0:
164 self._message.setText("")
167 error = self._errorLog.peek_message()
168 self._message.setText(error.message)
169 self._severityLabel.setPixmap(self._icons[error.level])
172 class QHtmlDelegate(QtGui.QStyledItemDelegate):
174 # @bug Not showing all of a message
176 def paint(self, painter, option, index):
177 newOption = QtGui.QStyleOptionViewItemV4(option)
178 self.initStyleOption(newOption, index)
179 if newOption.widget is not None:
180 style = newOption.widget.style()
182 style = QtGui.QApplication.style()
184 doc = QtGui.QTextDocument()
185 doc.setHtml(newOption.text)
186 doc.setTextWidth(newOption.rect.width())
189 style.drawControl(QtGui.QStyle.CE_ItemViewItem, newOption, painter)
191 ctx = QtGui.QAbstractTextDocumentLayout.PaintContext()
192 if newOption.state & QtGui.QStyle.State_Selected:
193 ctx.palette.setColor(
195 newOption.palette.color(
196 QtGui.QPalette.Active,
197 QtGui.QPalette.HighlightedText
201 ctx.palette.setColor(
203 newOption.palette.color(
204 QtGui.QPalette.Active,
209 textRect = style.subElementRect(QtGui.QStyle.SE_ItemViewItemText, newOption)
211 painter.translate(textRect.topLeft())
212 painter.setClipRect(textRect.translated(-textRect.topLeft()))
213 doc.documentLayout().draw(painter, ctx)
216 def sizeHint(self, option, index):
217 newOption = QtGui.QStyleOptionViewItemV4(option)
218 self.initStyleOption(newOption, index)
220 doc = QtGui.QTextDocument()
221 doc.setHtml(newOption.text)
222 doc.setTextWidth(newOption.rect.width())
223 size = QtCore.QSize(doc.idealWidth(), doc.size().height())
227 def _null_set_stackable(window, isStackable):
231 def _maemo_set_stackable(window, isStackable):
232 window.setAttribute(QtCore.Qt.WA_Maemo5StackedWindow, isStackable)
236 QtCore.Qt.WA_Maemo5StackedWindow
237 set_stackable = _maemo_set_stackable
238 except AttributeError:
239 set_stackable = _null_set_stackable
242 def _null_set_autorient(window, isStackable):
246 def _maemo_set_autorient(window, isStackable):
247 window.setAttribute(QtCore.Qt.WA_Maemo5StackedWindow, isStackable)
251 QtCore.Qt.WA_Maemo5AutoOrientation
252 set_autorient = _maemo_set_autorient
253 except AttributeError:
254 set_autorient = _null_set_autorient
257 def _null_set_landscape(window, isStackable):
261 def _maemo_set_landscape(window, isStackable):
262 window.setAttribute(QtCore.Qt.WA_Maemo5StackedWindow, isStackable)
266 QtCore.Qt.WA_Maemo5LandscapeOrientation
267 set_landscape = _maemo_set_landscape
268 except AttributeError:
269 set_landscape = _null_set_landscape
272 def _null_set_portrait(window, isStackable):
276 def _maemo_set_portrait(window, isStackable):
277 window.setAttribute(QtCore.Qt.WA_Maemo5StackedWindow, isStackable)
281 QtCore.Qt.WA_Maemo5PortraitOrientation
282 set_portrait = _maemo_set_portrait
283 except AttributeError:
284 set_portrait = _null_set_portrait
287 def _null_show_progress_indicator(window, isStackable):
291 def _maemo_show_progress_indicator(window, isStackable):
292 window.setAttribute(QtCore.Qt.WA_Maemo5StackedWindow, isStackable)
296 QtCore.Qt.WA_Maemo5ShowProgressIndicator
297 show_progress_indicator = _maemo_show_progress_indicator
298 except AttributeError:
299 show_progress_indicator = _null_show_progress_indicator
302 def _null_mark_numbers_preferred(widget):
306 def _newqt_mark_numbers_preferred(widget):
307 widget.setInputMethodHints(QtCore.Qt.ImhPreferNumbers)
311 QtCore.Qt.ImhPreferNumbers
312 mark_numbers_preferred = _newqt_mark_numbers_preferred
313 except AttributeError:
314 mark_numbers_preferred = _null_mark_numbers_preferred
317 def screen_orientation():
318 geom = QtGui.QApplication.desktop().screenGeometry()
319 if geom.width() <= geom.height():
320 return QtCore.Qt.Vertical
322 return QtCore.Qt.Horizontal
325 def _null_get_theme_icon(iconNames, fallback = None):
326 icon = fallback if fallback is not None else QtGui.QIcon()
330 def _newqt_get_theme_icon(iconNames, fallback = None):
331 for iconName in iconNames:
332 if QtGui.QIcon.hasThemeIcon(iconName):
333 icon = QtGui.QIcon.fromTheme(iconName)
336 icon = fallback if fallback is not None else QtGui.QIcon()
341 QtGui.QIcon.fromTheme
342 get_theme_icon = _newqt_get_theme_icon
343 except AttributeError:
344 get_theme_icon = _null_get_theme_icon