X-Git-Url: https://vcs.maemo.org/git/?a=blobdiff_plain;f=src%2Futil%2Fqui_utils.py;h=ada4567556ab3112b66d899f8d450798338030c1;hb=3103942d7abc6bb944807b7de3ba7024c73effd6;hp=7a9ac5206b055e754294691797f3fa2e03654cbf;hpb=5511579804f952c3403d6fb1c430254b79e5a791;p=gc-dialer diff --git a/src/util/qui_utils.py b/src/util/qui_utils.py index 7a9ac52..ada4567 100644 --- a/src/util/qui_utils.py +++ b/src/util/qui_utils.py @@ -1,5 +1,6 @@ import sys import contextlib +import datetime import logging from PyQt4 import QtCore @@ -19,15 +20,25 @@ def notify_error(log): log.push_exception() +@contextlib.contextmanager +def notify_busy(log, message): + log.push_busy(message) + try: + yield + finally: + log.pop(message) + + class ErrorMessage(object): - LEVEL_BUSY = "busy" - LEVEL_INFO = "info" - LEVEL_ERROR = "error" + LEVEL_ERROR = 0 + LEVEL_BUSY = 1 + LEVEL_INFO = 2 def __init__(self, message, level): self._message = message self._level = level + self._time = datetime.datetime.now() @property def level(self): @@ -37,6 +48,9 @@ class ErrorMessage(object): def message(self): return self._message + def __repr__(self): + return "%s.%s(%r, %r)" % (__name__, self.__class__.__name__, self._message, self._level) + class QErrorLog(QtCore.QObject): @@ -48,6 +62,7 @@ class QErrorLog(QtCore.QObject): self._messages = [] def push_busy(self, message): + _moduleLogger.info("Entering state: %s" % message) self._push_message(message, ErrorMessage.LEVEL_BUSY) def push_message(self, message): @@ -65,6 +80,7 @@ class QErrorLog(QtCore.QObject): if message is None: del self._messages[0] else: + _moduleLogger.info("Exiting state: %s" % message) messageIndex = [ i for (i, error) in enumerate(self._messages) @@ -80,6 +96,8 @@ class QErrorLog(QtCore.QObject): def _push_message(self, message, level): self._messages.append(ErrorMessage(message, level)) + # Sort is defined as stable, so this should be fine + self._messages.sort(key=lambda x: x.level) self.messagePushed.emit() def __len__(self): @@ -88,52 +106,66 @@ class QErrorLog(QtCore.QObject): class ErrorDisplay(object): + _SENTINEL_ICON = QtGui.QIcon() + def __init__(self, errorLog): self._errorLog = errorLog self._errorLog.messagePushed.connect(self._on_message_pushed) self._errorLog.messagePopped.connect(self._on_message_popped) - self._icons = { - ErrorMessage.LEVEL_BUSY: - get_theme_icon( - #("process-working", "gtk-refresh") - ("gtk-refresh", ) - ).pixmap(32, 32), - ErrorMessage.LEVEL_INFO: - get_theme_icon( - ("dialog-information", "general_notes", "gtk-info") - ).pixmap(32, 32), - ErrorMessage.LEVEL_ERROR: - get_theme_icon( - ("dialog-error", "app_install_error", "gtk-dialog-error") - ).pixmap(32, 32), - } + self._icons = None self._severityLabel = QtGui.QLabel() self._severityLabel.setAlignment(QtCore.Qt.AlignLeft | QtCore.Qt.AlignVCenter) self._message = QtGui.QLabel() self._message.setText("Boo") self._message.setAlignment(QtCore.Qt.AlignLeft | QtCore.Qt.AlignVCenter) + self._message.setWordWrap(True) - closeIcon = get_theme_icon(("window-close", "general_close", "gtk-close")) - self._closeLabel = QtGui.QPushButton(closeIcon, "") - self._closeLabel.clicked.connect(self._on_close) + self._closeLabel = None self._controlLayout = QtGui.QHBoxLayout() self._controlLayout.addWidget(self._severityLabel, 1, QtCore.Qt.AlignCenter) self._controlLayout.addWidget(self._message, 1000) - self._controlLayout.addWidget(self._closeLabel, 1, QtCore.Qt.AlignCenter) - self._topLevelLayout = QtGui.QHBoxLayout() - self._topLevelLayout.addLayout(self._controlLayout) self._widget = QtGui.QWidget() - self._widget.setLayout(self._topLevelLayout) + self._widget.setLayout(self._controlLayout) self._widget.hide() @property def toplevel(self): return self._widget + def _show_error(self): + if self._icons is None: + self._icons = { + ErrorMessage.LEVEL_BUSY: + get_theme_icon( + #("process-working", "view-refresh", "general_refresh", "gtk-refresh") + ("view-refresh", "general_refresh", "gtk-refresh", ) + ).pixmap(32, 32), + ErrorMessage.LEVEL_INFO: + get_theme_icon( + ("dialog-information", "general_notes", "gtk-info") + ).pixmap(32, 32), + ErrorMessage.LEVEL_ERROR: + get_theme_icon( + ("dialog-error", "app_install_error", "gtk-dialog-error") + ).pixmap(32, 32), + } + if self._closeLabel is None: + closeIcon = get_theme_icon(("window-close", "general_close", "gtk-close"), self._SENTINEL_ICON) + if closeIcon is not self._SENTINEL_ICON: + self._closeLabel = QtGui.QPushButton(closeIcon, "") + else: + self._closeLabel = QtGui.QPushButton("X") + self._closeLabel.clicked.connect(self._on_close) + self._controlLayout.addWidget(self._closeLabel, 1, QtCore.Qt.AlignCenter) + error = self._errorLog.peek_message() + self._message.setText(error.message) + self._severityLabel.setPixmap(self._icons[error.level]) + self._widget.show() + @QtCore.pyqtSlot() @QtCore.pyqtSlot(bool) @misc.log_exception(_moduleLogger) @@ -143,11 +175,7 @@ class ErrorDisplay(object): @QtCore.pyqtSlot() @misc.log_exception(_moduleLogger) def _on_message_pushed(self): - if 1 <= len(self._errorLog) and self._widget.isHidden(): - error = self._errorLog.peek_message() - self._message.setText(error.message) - self._severityLabel.setPixmap(self._icons[error.level]) - self._widget.show() + self._show_error() @QtCore.pyqtSlot() @misc.log_exception(_moduleLogger) @@ -156,14 +184,16 @@ class ErrorDisplay(object): self._message.setText("") self._widget.hide() else: - error = self._errorLog.peek_message() - self._message.setText(error.message) - self._severityLabel.setPixmap(self._icons[error.level]) + self._show_error() class QHtmlDelegate(QtGui.QStyledItemDelegate): - # @bug Not showing all of a message + UNDEFINED_SIZE = -1 + + def __init__(self, *args, **kwd): + QtGui.QStyledItemDelegate.__init__(*((self, ) + args), **kwd) + self._width = self.UNDEFINED_SIZE def paint(self, painter, option, index): newOption = QtGui.QStyleOptionViewItemV4(option) @@ -205,17 +235,58 @@ class QHtmlDelegate(QtGui.QStyledItemDelegate): doc.documentLayout().draw(painter, ctx) painter.restore() + def setWidth(self, width, model): + if self._width == width: + return + self._width = width + for c in xrange(model.rowCount()): + cItem = model.item(c, 0) + for r in xrange(model.rowCount()): + rItem = cItem.child(r, 0) + rIndex = model.indexFromItem(rItem) + self.sizeHintChanged.emit(rIndex) + return + def sizeHint(self, option, index): newOption = QtGui.QStyleOptionViewItemV4(option) self.initStyleOption(newOption, index) doc = QtGui.QTextDocument() doc.setHtml(newOption.text) - doc.setTextWidth(newOption.rect.width()) + if self._width != self.UNDEFINED_SIZE: + width = self._width + else: + width = newOption.rect.width() + doc.setTextWidth(width) size = QtCore.QSize(doc.idealWidth(), doc.size().height()) return size +class QSignalingMainWindow(QtGui.QMainWindow): + + closed = QtCore.pyqtSignal() + hidden = QtCore.pyqtSignal() + shown = QtCore.pyqtSignal() + + def __init__(self, *args, **kwd): + QtGui.QMainWindow.__init__(*((self, )+args), **kwd) + + def closeEvent(self, event): + val = QtGui.QMainWindow.closeEvent(self, event) + self.closed.emit() + return val + + def hideEvent(self, event): + val = QtGui.QMainWindow.hideEvent(self, event) + self.hidden.emit() + return val + + def showEvent(self, event): + val = QtGui.QMainWindow.showEvent(self, event) + self.shown.emit() + return val + + def _null_set_stackable(window, isStackable): pass @@ -236,7 +307,7 @@ def _null_set_autorient(window, isStackable): def _maemo_set_autorient(window, isStackable): - window.setAttribute(QtCore.Qt.WA_Maemo5StackedWindow, isStackable) + window.setAttribute(QtCore.Qt.WA_Maemo5AutoOrientation, isStackable) try: @@ -246,34 +317,35 @@ except AttributeError: set_autorient = _null_set_autorient -def _null_set_landscape(window, isStackable): - pass - - -def _maemo_set_landscape(window, isStackable): - window.setAttribute(QtCore.Qt.WA_Maemo5StackedWindow, isStackable) - - -try: - QtCore.Qt.WA_Maemo5LandscapeOrientation - set_landscape = _maemo_set_landscape -except AttributeError: - set_landscape = _null_set_landscape +def screen_orientation(): + geom = QtGui.QApplication.desktop().screenGeometry() + if geom.width() <= geom.height(): + return QtCore.Qt.Vertical + else: + return QtCore.Qt.Horizontal -def _null_set_portrait(window, isStackable): +def _null_set_window_orientation(window, orientation): pass -def _maemo_set_portrait(window, isStackable): - window.setAttribute(QtCore.Qt.WA_Maemo5StackedWindow, isStackable) +def _maemo_set_window_orientation(window, orientation): + if orientation == QtCore.Qt.Vertical: + oldHint = QtCore.Qt.WA_Maemo5LandscapeOrientation + newHint = QtCore.Qt.WA_Maemo5PortraitOrientation + elif orientation == QtCore.Qt.Horizontal: + oldHint = QtCore.Qt.WA_Maemo5PortraitOrientation + newHint = QtCore.Qt.WA_Maemo5LandscapeOrientation + window.setAttribute(oldHint, False) + window.setAttribute(newHint, True) try: + QtCore.Qt.WA_Maemo5LandscapeOrientation QtCore.Qt.WA_Maemo5PortraitOrientation - set_portrait = _maemo_set_portrait + set_window_orientation = _maemo_set_window_orientation except AttributeError: - set_portrait = _null_set_portrait + set_window_orientation = _null_set_window_orientation def _null_show_progress_indicator(window, isStackable): @@ -281,7 +353,7 @@ def _null_show_progress_indicator(window, isStackable): def _maemo_show_progress_indicator(window, isStackable): - window.setAttribute(QtCore.Qt.WA_Maemo5StackedWindow, isStackable) + window.setAttribute(QtCore.Qt.WA_Maemo5ShowProgressIndicator, isStackable) try: @@ -306,14 +378,6 @@ except AttributeError: mark_numbers_preferred = _null_mark_numbers_preferred -def screen_orientation(): - geom = QtGui.QApplication.desktop().screenGeometry() - if geom.width() <= geom.height(): - return QtCore.Qt.Vertical - else: - return QtCore.Qt.Horizontal - - def _null_get_theme_icon(iconNames, fallback = None): icon = fallback if fallback is not None else QtGui.QIcon() return icon