#!/usr/bin/env python
import math
+import logging
from PyQt4 import QtGui
from PyQt4 import QtCore
+try:
+ from util import misc as misc_utils
+except ImportError:
+ class misc_utils(object):
+
+ @staticmethod
+ def log_exception(logger):
+
+ def wrapper(func):
+ return func
+ return wrapper
+
+
+_moduleLogger = logging.getLogger(__name__)
+
_TWOPI = 2 * math.pi
class PieFiling(object):
- INNER_RADIUS_DEFAULT = 32
- OUTER_RADIUS_DEFAULT = 128
+ INNER_RADIUS_DEFAULT = 64
+ OUTER_RADIUS_DEFAULT = 192
SELECTION_CENTER = -1
SELECTION_NONE = -2
class PieArtist(object):
- ICON_SIZE_DEFAULT = 32
+ ICON_SIZE_DEFAULT = 48
+
+ SHAPE_CIRCLE = "circle"
+ SHAPE_SQUARE = "square"
+ DEFAULT_SHAPE = SHAPE_SQUARE
def __init__(self, filing):
self._filing = filing
if selectionIndex == PieFiling.SELECTION_CENTER and self._filing.center().isEnabled():
painter.setBrush(self.palette.highlight())
else:
- painter.setBrush(self.palette.background())
+ painter.setBrush(self.palette.window())
+ painter.setPen(self.palette.mid().color())
- painter.fillRect(self._canvas.rect(), painter.brush())
+ painter.drawRect(self._canvas.rect())
self._paint_center_foreground(painter, selectionIndex)
return self._canvas
elif numChildren == 1:
if selectionIndex == 0 and self._filing[0].isEnabled():
painter.setBrush(self.palette.highlight())
else:
- painter.setBrush(self.palette.background())
+ painter.setBrush(self.palette.window())
painter.fillRect(self._canvas.rect(), painter.brush())
else:
painter = QtGui.QPainter(mask)
painter.setPen(QtCore.Qt.color1)
painter.setBrush(QtCore.Qt.color1)
- painter.drawEllipse(mask.rect().adjusted(0, 0, -1, -1))
+ if self.DEFAULT_SHAPE == self.SHAPE_SQUARE:
+ painter.drawRect(mask.rect())
+ elif self.DEFAULT_SHAPE == self.SHAPE_CIRCLE:
+ painter.drawEllipse(mask.rect().adjusted(0, 0, -1, -1))
+ else:
+ raise NotImplementedError(self.DEFAULT_SHAPE)
def _paint_slice_background(self, painter, adjustmentRect, i, selectionIndex):
+ if self.DEFAULT_SHAPE == self.SHAPE_SQUARE:
+ currentWidth = adjustmentRect.width()
+ newWidth = math.sqrt(2) * currentWidth
+ dx = (newWidth - currentWidth) / 2
+ adjustmentRect = adjustmentRect.adjusted(-dx, -dx, dx, dx)
+ elif self.DEFAULT_SHAPE == self.SHAPE_CIRCLE:
+ pass
+ else:
+ raise NotImplementedError(self.DEFAULT_SHAPE)
+
if i == selectionIndex and self._filing[i].isEnabled():
painter.setBrush(self.palette.highlight())
else:
- painter.setBrush(self.palette.background())
+ painter.setBrush(self.palette.window())
painter.setPen(self.palette.mid().color())
a = self._filing._index_to_angle(i, True)
brush = self.palette.highlight()
else:
pen = self.palette.mid()
- brush = self.palette.background()
+ brush = self.palette.window()
else:
if action.isEnabled():
- pen = self.palette.text()
+ pen = self.palette.windowText()
else:
pen = self.palette.mid()
- brush = self.palette.background()
+ brush = self.palette.window()
leftX = x - averageWidth + iconWidth
topY = y + textHeight/2
painter.drawText(leftX, topY, text)
def _paint_center_background(self, painter, adjustmentRect, selectionIndex):
- dark = self.palette.dark().color()
+ dark = self.palette.mid().color()
light = self.palette.light().color()
if selectionIndex == PieFiling.SELECTION_CENTER and self._filing.center().isEnabled():
background = self.palette.highlight().color()
else:
- background = self.palette.background().color()
+ background = self.palette.window().color()
innerRadius = self._cachedInnerRadius
adjustmentCenterPos = adjustmentRect.center()
painter.setBrush(QtCore.Qt.NoBrush)
painter.drawEllipse(innerRect)
- painter.setPen(QtGui.QPen(dark, 1))
- painter.setBrush(QtCore.Qt.NoBrush)
- painter.drawEllipse(adjustmentRect)
-
- r = QtCore.QRect(innerRect)
- innerCenter = r.center()
- innerRect.setLeft(innerCenter.x() + ((r.left() - innerCenter.x()) / 3) * 1)
- innerRect.setRight(innerCenter.x() + ((r.right() - innerCenter.x()) / 3) * 1)
- innerRect.setTop(innerCenter.y() + ((r.top() - innerCenter.y()) / 3) * 1)
- innerRect.setBottom(innerCenter.y() + ((r.bottom() - innerCenter.y()) / 3) * 1)
+ if self.DEFAULT_SHAPE == self.SHAPE_SQUARE:
+ pass
+ elif self.DEFAULT_SHAPE == self.SHAPE_CIRCLE:
+ painter.setPen(QtGui.QPen(dark, 1))
+ painter.setBrush(QtCore.Qt.NoBrush)
+ painter.drawEllipse(adjustmentRect)
+ else:
+ raise NotImplementedError(self.DEFAULT_SHAPE)
def _paint_center_foreground(self, painter, selectionIndex):
centerPos = self._canvas.rect().center()
def sizeHint(self):
return self._artist.pieSize()
+ @misc_utils.log_exception(_moduleLogger)
def showEvent(self, showEvent):
mask = self._artist.show(self.palette())
self.setMask(mask)
QtGui.QWidget.showEvent(self, showEvent)
+ @misc_utils.log_exception(_moduleLogger)
def hideEvent(self, hideEvent):
self._artist.hide()
self._selectionIndex = PieFiling.SELECTION_NONE
QtGui.QWidget.hideEvent(self, hideEvent)
+ @misc_utils.log_exception(_moduleLogger)
def paintEvent(self, paintEvent):
canvas = self._artist.paint(self._selectionIndex)
QtGui.QWidget.paintEvent(self, paintEvent)
def selectAt(self, index):
+ oldIndex = self._selectionIndex
self._selectionIndex = index
- self.update()
+ if self.isVisible():
+ self.update()
class QPieButton(QtGui.QWidget):
aboutToShow = QtCore.pyqtSignal()
aboutToHide = QtCore.pyqtSignal()
+ BUTTON_RADIUS = 24
+ DELAY = 250
+
def __init__(self, buttonSlice, parent = None):
+ # @bug Artifacts on Maemo 5 due to window 3D effects, find way to disable them for just these?
+ # @bug The pie's are being pushed back on screen on Maemo, leading to coordinate issues
QtGui.QWidget.__init__(self, parent)
self._cachedCenterPosition = self.rect().center()
self._buttonFiling = PieFiling()
self._buttonFiling.set_center(buttonSlice)
+ self._buttonFiling.setOuterRadius(self.BUTTON_RADIUS)
self._buttonArtist = PieArtist(self._buttonFiling)
- centerSize = self._buttonArtist.centerSize()
- self._buttonFiling.setOuterRadius(max(centerSize.width(), centerSize.height()))
self._poppedUp = False
+ self._delayPopupTimer = QtCore.QTimer()
+ self._delayPopupTimer.setInterval(self.DELAY)
+ self._delayPopupTimer.setSingleShot(True)
+ self._delayPopupTimer.timeout.connect(self._on_delayed_popup)
+ self._popupLocation = None
+
self._mousePosition = None
self.setFocusPolicy(QtCore.Qt.StrongFocus)
def setOuterRadius(self, radius):
self._filing.setOuterRadius(radius)
- def sizeHint(self):
- return self._buttonArtist.pieSize()
+ def buttonRadius(self):
+ return self._buttonFiling.outerRadius()
+
+ def setButtonRadius(self, radius):
+ self._buttonFiling.setOuterRadius(radius)
+ self._buttonArtist.show(self.palette())
+ def minimumSizeHint(self):
+ return self._buttonArtist.centerSize()
+
+ @misc_utils.log_exception(_moduleLogger)
def mousePressEvent(self, mouseEvent):
- self._popup_child(mouseEvent.globalPos())
lastSelection = self._selectionIndex
lastMousePos = mouseEvent.pos()
self._mousePosition = lastMousePos
self._update_selection(self._cachedCenterPosition)
- if lastSelection != self._selectionIndex:
- self.highlighted.emit(self._selectionIndex)
- self._display.selectAt(self._selectionIndex)
+ self.highlighted.emit(self._selectionIndex)
+
+ self._display.selectAt(self._selectionIndex)
+ self._popupLocation = mouseEvent.globalPos()
+ self._delayPopupTimer.start()
+
+ @misc_utils.log_exception(_moduleLogger)
+ def _on_delayed_popup(self):
+ assert self._popupLocation is not None
+ self._popup_child(self._popupLocation)
+ @misc_utils.log_exception(_moduleLogger)
def mouseMoveEvent(self, mouseEvent):
lastSelection = self._selectionIndex
self._update_selection(lastMousePos)
else:
# Relative
- self._update_selection(self._cachedCenterPosition + (lastMousePos - self._mousePosition))
+ self._update_selection(
+ self._cachedCenterPosition + (lastMousePos - self._mousePosition),
+ ignoreOuter = True,
+ )
if lastSelection != self._selectionIndex:
self.highlighted.emit(self._selectionIndex)
self._display.selectAt(self._selectionIndex)
+ if self._selectionIndex != PieFiling.SELECTION_CENTER and self._delayPopupTimer.isActive():
+ self._on_delayed_popup()
+
+ @misc_utils.log_exception(_moduleLogger)
def mouseReleaseEvent(self, mouseEvent):
+ self._delayPopupTimer.stop()
+ self._popupLocation = None
+
lastSelection = self._selectionIndex
lastMousePos = mouseEvent.pos()
self._update_selection(lastMousePos)
else:
# Relative
- self._update_selection(self._cachedCenterPosition + (lastMousePos - self._mousePosition))
+ self._update_selection(
+ self._cachedCenterPosition + (lastMousePos - self._mousePosition),
+ ignoreOuter = True,
+ )
self._mousePosition = None
self._activate_at(self._selectionIndex)
self._hide_child()
+ @misc_utils.log_exception(_moduleLogger)
def keyPressEvent(self, keyEvent):
if keyEvent.key() in [QtCore.Qt.Key_Right, QtCore.Qt.Key_Down, QtCore.Qt.Key_Tab]:
self._popup_child(QtGui.QCursor.pos())
self._select_at(PieFiling.SELECTION_CENTER)
self._display.selectAt(self._selectionIndex)
elif keyEvent.key() in [QtCore.Qt.Key_Return, QtCore.Qt.Key_Enter, QtCore.Qt.Key_Space]:
+ self._delayPopupTimer.stop()
+ self._popupLocation = None
self._activate_at(self._selectionIndex)
self._hide_child()
elif keyEvent.key() in [QtCore.Qt.Key_Escape, QtCore.Qt.Key_Backspace]:
+ self._delayPopupTimer.stop()
+ self._popupLocation = None
self._activate_at(PieFiling.SELECTION_NONE)
self._hide_child()
else:
QtGui.QWidget.keyPressEvent(self, keyEvent)
+ @misc_utils.log_exception(_moduleLogger)
+ def resizeEvent(self, resizeEvent):
+ self.setButtonRadius(min(resizeEvent.size().width(), resizeEvent.size().height()) / 2 - 1)
+ QtGui.QWidget.resizeEvent(self, resizeEvent)
+
+ @misc_utils.log_exception(_moduleLogger)
def showEvent(self, showEvent):
self._buttonArtist.show(self.palette())
self._cachedCenterPosition = self.rect().center()
QtGui.QWidget.showEvent(self, showEvent)
+ @misc_utils.log_exception(_moduleLogger)
def hideEvent(self, hideEvent):
self._display.hide()
self._select_at(PieFiling.SELECTION_NONE)
QtGui.QWidget.hideEvent(self, hideEvent)
+ @misc_utils.log_exception(_moduleLogger)
def paintEvent(self, paintEvent):
+ self.setButtonRadius(min(self.rect().width(), self.rect().height()) / 2 - 1)
if self._poppedUp:
canvas = self._buttonArtist.paint(PieFiling.SELECTION_CENTER)
else:
QtGui.QWidget.paintEvent(self, paintEvent)
+ def __iter__(self):
+ return iter(self._filing)
+
+ def __len__(self):
+ return len(self._filing)
+
def _popup_child(self, position):
self._poppedUp = True
self.aboutToShow.emit()
+ self._delayPopupTimer.stop()
+ self._popupLocation = None
+
position = position - QtCore.QPoint(self._filing.outerRadius(), self._filing.outerRadius())
self._display.move(position)
self._display.show()
def _select_at(self, index):
self._selectionIndex = index
- def _update_selection(self, lastMousePos):
+ def _update_selection(self, lastMousePos, ignoreOuter = False):
radius = _radius_at(self._cachedCenterPosition, lastMousePos)
if radius < self._filing.innerRadius():
self._select_at(PieFiling.SELECTION_CENTER)
- elif radius <= self._filing.outerRadius():
+ elif radius <= self._filing.outerRadius() or ignoreOuter:
self._select_at(self.indexAt(lastMousePos))
else:
self._select_at(PieFiling.SELECTION_NONE)
def sizeHint(self):
return self._artist.pieSize()
+ @misc_utils.log_exception(_moduleLogger)
def mousePressEvent(self, mouseEvent):
lastSelection = self._selectionIndex
self.highlighted.emit(self._selectionIndex)
self.update()
+ @misc_utils.log_exception(_moduleLogger)
def mouseMoveEvent(self, mouseEvent):
lastSelection = self._selectionIndex
self.highlighted.emit(self._selectionIndex)
self.update()
+ @misc_utils.log_exception(_moduleLogger)
def mouseReleaseEvent(self, mouseEvent):
lastSelection = self._selectionIndex
self._activate_at(self._selectionIndex)
self.update()
+ @misc_utils.log_exception(_moduleLogger)
def keyPressEvent(self, keyEvent):
if keyEvent.key() in [QtCore.Qt.Key_Right, QtCore.Qt.Key_Down, QtCore.Qt.Key_Tab]:
if self._selectionIndex != len(self._filing) - 1:
else:
QtGui.QWidget.keyPressEvent(self, keyEvent)
+ @misc_utils.log_exception(_moduleLogger)
def showEvent(self, showEvent):
self.aboutToShow.emit()
self._cachedCenterPosition = self.rect().center()
QtGui.QWidget.showEvent(self, showEvent)
+ @misc_utils.log_exception(_moduleLogger)
def hideEvent(self, hideEvent):
self._artist.hide()
self._selectionIndex = PieFiling.SELECTION_NONE
QtGui.QWidget.hideEvent(self, hideEvent)
+ @misc_utils.log_exception(_moduleLogger)
def paintEvent(self, paintEvent):
canvas = self._artist.paint(self._selectionIndex)
QtGui.QWidget.paintEvent(self, paintEvent)
+ def __iter__(self):
+ return iter(self._filing)
+
+ def __len__(self):
+ return len(self._filing)
+
def _select_at(self, index):
self._selectionIndex = index
self.hide()
+def init_pies():
+ PieFiling.NULL_CENTER.setEnabled(False)
+
+
def _print(msg):
print msg
if __name__ == "__main__":
app = QtGui.QApplication([])
- PieFiling.NULL_CENTER.setEnabled(False)
+ init_pies()
if False:
pie = QPieMenu()