6 from PyQt4 import QtGui
7 from PyQt4 import QtCore
10 from util import misc as misc_utils
12 class misc_utils(object):
15 def log_exception(logger):
22 _moduleLogger = logging.getLogger(__name__)
28 class EIGHT_SLICE_PIE(object):
40 MAX_ANGULAR_SLICES = 8
54 SLICE_DIRECTION_NAMES = [
67 def _radius_at(center, pos):
72 radius = math.sqrt(xDelta ** 2 + yDelta ** 2)
76 def _angle_at(center, pos):
81 radius = math.sqrt(xDelta ** 2 + yDelta ** 2)
82 angle = math.acos(xDelta / radius)
84 angle = _TWOPI - angle
89 class QActionPieItem(object):
91 def __init__(self, action, weight = 1):
98 def setWeight(self, weight):
104 def setEnabled(self, enabled = True):
105 self._action.setEnabled(enabled)
108 return self._action.isEnabled()
111 class PieFiling(object):
113 INNER_RADIUS_DEFAULT = 64
114 OUTER_RADIUS_DEFAULT = 192
116 SELECTION_CENTER = -1
119 NULL_CENTER = QActionPieItem(QtGui.QAction(None))
122 self._innerRadius = self.INNER_RADIUS_DEFAULT
123 self._outerRadius = self.OUTER_RADIUS_DEFAULT
125 self._center = self.NULL_CENTER
127 self._cacheIndexToAngle = {}
128 self._cacheTotalWeight = 0
130 def insertItem(self, item, index = -1):
131 self._children.insert(index, item)
132 self._invalidate_cache()
134 def removeItemAt(self, index):
135 item = self._children.pop(index)
136 self._invalidate_cache()
138 def set_center(self, item):
140 item = self.NULL_CENTER
147 del self._children[:]
148 self._center = self.NULL_CENTER
149 self._invalidate_cache()
151 def itemAt(self, index):
152 return self._children[index]
154 def indexAt(self, center, point):
155 return self._angle_to_index(_angle_at(center, point))
157 def innerRadius(self):
158 return self._innerRadius
160 def setInnerRadius(self, radius):
161 self._innerRadius = radius
163 def outerRadius(self):
164 return self._outerRadius
166 def setOuterRadius(self, radius):
167 self._outerRadius = radius
170 return iter(self._children)
173 return len(self._children)
175 def __getitem__(self, index):
176 return self._children[index]
178 def _invalidate_cache(self):
179 self._cacheIndexToAngle.clear()
180 self._cacheTotalWeight = sum(child.weight() for child in self._children)
181 if self._cacheTotalWeight == 0:
182 self._cacheTotalWeight = 1
184 def _index_to_angle(self, index, isShifted):
185 key = index, isShifted
186 if key in self._cacheIndexToAngle:
187 return self._cacheIndexToAngle[key]
188 index = index % len(self._children)
190 baseAngle = _TWOPI / self._cacheTotalWeight
195 angle -= (self._children[0].weight() * baseAngle) / 2
197 angle -= baseAngle / 2
201 for i, child in enumerate(self._children):
204 angle += child.weight() * baseAngle
205 while _TWOPI < angle:
208 self._cacheIndexToAngle[key] = angle
211 def _angle_to_index(self, angle):
212 numChildren = len(self._children)
214 return self.SELECTION_CENTER
216 baseAngle = _TWOPI / self._cacheTotalWeight
218 iterAngle = math.pi / 2 - (self.itemAt(0).weight() * baseAngle) / 2
222 oldIterAngle = iterAngle
223 for index, child in enumerate(self._children):
224 iterAngle += child.weight() * baseAngle
225 if oldIterAngle < angle and angle <= iterAngle:
226 return index - 1 if index != 0 else numChildren - 1
227 elif oldIterAngle < (angle + _TWOPI) and (angle + _TWOPI <= iterAngle):
228 return index - 1 if index != 0 else numChildren - 1
229 oldIterAngle = iterAngle
232 class PieArtist(object):
234 ICON_SIZE_DEFAULT = 48
236 SHAPE_CIRCLE = "circle"
237 SHAPE_SQUARE = "square"
238 DEFAULT_SHAPE = SHAPE_SQUARE
240 def __init__(self, filing):
241 self._filing = filing
243 self._cachedOuterRadius = self._filing.outerRadius()
244 self._cachedInnerRadius = self._filing.innerRadius()
245 canvasSize = self._cachedOuterRadius * 2 + 1
246 self._canvas = QtGui.QPixmap(canvasSize, canvasSize)
251 diameter = self._filing.outerRadius() * 2 + 1
252 return QtCore.QSize(diameter, diameter)
254 def centerSize(self):
255 painter = QtGui.QPainter(self._canvas)
256 text = self._filing.center().action().text()
257 fontMetrics = painter.fontMetrics()
259 textBoundingRect = fontMetrics.boundingRect(text)
261 textBoundingRect = QtCore.QRect()
262 textWidth = textBoundingRect.width()
263 textHeight = textBoundingRect.height()
266 textWidth + self.ICON_SIZE_DEFAULT,
267 max(textHeight, self.ICON_SIZE_DEFAULT),
270 def show(self, palette):
271 self.palette = palette
274 self._cachedOuterRadius != self._filing.outerRadius() or
275 self._cachedInnerRadius != self._filing.innerRadius()
277 self._cachedOuterRadius = self._filing.outerRadius()
278 self._cachedInnerRadius = self._filing.innerRadius()
279 self._canvas = self._canvas.scaled(self.pieSize())
281 if self._mask is None:
282 self._mask = QtGui.QBitmap(self._canvas.size())
283 self._mask.fill(QtCore.Qt.color0)
284 self._generate_mask(self._mask)
285 self._canvas.setMask(self._mask)
291 def paint(self, selectionIndex):
292 painter = QtGui.QPainter(self._canvas)
293 painter.setRenderHint(QtGui.QPainter.Antialiasing, True)
295 adjustmentRect = self._canvas.rect().adjusted(0, 0, -1, -1)
297 numChildren = len(self._filing)
299 if selectionIndex == PieFiling.SELECTION_CENTER and self._filing.center().isEnabled():
300 painter.setBrush(self.palette.highlight())
302 painter.setBrush(self.palette.window())
303 painter.setPen(self.palette.mid().color())
305 painter.drawRect(self._canvas.rect())
306 self._paint_center_foreground(painter, selectionIndex)
308 elif numChildren == 1:
309 if selectionIndex == 0 and self._filing[0].isEnabled():
310 painter.setBrush(self.palette.highlight())
312 painter.setBrush(self.palette.window())
314 painter.fillRect(self._canvas.rect(), painter.brush())
316 for i in xrange(len(self._filing)):
317 self._paint_slice_background(painter, adjustmentRect, i, selectionIndex)
319 self._paint_center_background(painter, adjustmentRect, selectionIndex)
320 self._paint_center_foreground(painter, selectionIndex)
322 for i in xrange(len(self._filing)):
323 self._paint_slice_foreground(painter, i, selectionIndex)
327 def _generate_mask(self, mask):
329 Specifies on the mask the shape of the pie menu
331 painter = QtGui.QPainter(mask)
332 painter.setPen(QtCore.Qt.color1)
333 painter.setBrush(QtCore.Qt.color1)
334 if self.DEFAULT_SHAPE == self.SHAPE_SQUARE:
335 painter.drawRect(mask.rect())
336 elif self.DEFAULT_SHAPE == self.SHAPE_CIRCLE:
337 painter.drawEllipse(mask.rect().adjusted(0, 0, -1, -1))
339 raise NotImplementedError(self.DEFAULT_SHAPE)
341 def _paint_slice_background(self, painter, adjustmentRect, i, selectionIndex):
342 if self.DEFAULT_SHAPE == self.SHAPE_SQUARE:
343 currentWidth = adjustmentRect.width()
344 newWidth = math.sqrt(2) * currentWidth
345 dx = (newWidth - currentWidth) / 2
346 adjustmentRect = adjustmentRect.adjusted(-dx, -dx, dx, dx)
347 elif self.DEFAULT_SHAPE == self.SHAPE_CIRCLE:
350 raise NotImplementedError(self.DEFAULT_SHAPE)
352 if i == selectionIndex and self._filing[i].isEnabled():
353 painter.setBrush(self.palette.highlight())
355 painter.setBrush(self.palette.window())
356 painter.setPen(self.palette.mid().color())
358 a = self._filing._index_to_angle(i, True)
359 b = self._filing._index_to_angle(i + 1, True)
366 startAngleInDeg = (a * 360 * 16) / _TWOPI
367 sizeInDeg = (size * 360 * 16) / _TWOPI
368 painter.drawPie(adjustmentRect, int(startAngleInDeg), int(sizeInDeg))
370 def _paint_slice_foreground(self, painter, i, selectionIndex):
371 child = self._filing[i]
373 a = self._filing._index_to_angle(i, True)
374 b = self._filing._index_to_angle(i + 1, True)
377 middleAngle = (a + b) / 2
378 averageRadius = (self._cachedInnerRadius + self._cachedOuterRadius) / 2
380 sliceX = averageRadius * math.cos(middleAngle)
381 sliceY = - averageRadius * math.sin(middleAngle)
383 piePos = self._canvas.rect().center()
387 painter, child.action(), i == selectionIndex, pieX+sliceX, pieY+sliceY
390 def _paint_label(self, painter, action, isSelected, x, y):
392 fontMetrics = painter.fontMetrics()
394 textBoundingRect = fontMetrics.boundingRect(text)
396 textBoundingRect = QtCore.QRect()
397 textWidth = textBoundingRect.width()
398 textHeight = textBoundingRect.height()
400 icon = action.icon().pixmap(
401 QtCore.QSize(self.ICON_SIZE_DEFAULT, self.ICON_SIZE_DEFAULT),
405 iconWidth = icon.width()
406 iconHeight = icon.width()
407 averageWidth = (iconWidth + textWidth)/2
408 if not icon.isNull():
409 iconRect = QtCore.QRect(
416 painter.drawPixmap(iconRect, icon)
420 if action.isEnabled():
421 pen = self.palette.highlightedText()
422 brush = self.palette.highlight()
424 pen = self.palette.mid()
425 brush = self.palette.window()
427 if action.isEnabled():
428 pen = self.palette.windowText()
430 pen = self.palette.mid()
431 brush = self.palette.window()
433 leftX = x - averageWidth + iconWidth
434 topY = y + textHeight/2
435 painter.setPen(pen.color())
436 painter.setBrush(brush)
437 painter.drawText(leftX, topY, text)
439 def _paint_center_background(self, painter, adjustmentRect, selectionIndex):
440 dark = self.palette.mid().color()
441 light = self.palette.light().color()
442 if selectionIndex == PieFiling.SELECTION_CENTER and self._filing.center().isEnabled():
443 background = self.palette.highlight().color()
445 background = self.palette.window().color()
447 innerRadius = self._cachedInnerRadius
448 adjustmentCenterPos = adjustmentRect.center()
449 innerRect = QtCore.QRect(
450 adjustmentCenterPos.x() - innerRadius,
451 adjustmentCenterPos.y() - innerRadius,
456 painter.setPen(QtCore.Qt.NoPen)
457 painter.setBrush(background)
458 painter.drawPie(innerRect, 0, 360 * 16)
460 painter.setPen(QtGui.QPen(dark, 1))
461 painter.setBrush(QtCore.Qt.NoBrush)
462 painter.drawEllipse(innerRect)
464 if self.DEFAULT_SHAPE == self.SHAPE_SQUARE:
466 elif self.DEFAULT_SHAPE == self.SHAPE_CIRCLE:
467 painter.setPen(QtGui.QPen(dark, 1))
468 painter.setBrush(QtCore.Qt.NoBrush)
469 painter.drawEllipse(adjustmentRect)
471 raise NotImplementedError(self.DEFAULT_SHAPE)
473 def _paint_center_foreground(self, painter, selectionIndex):
474 centerPos = self._canvas.rect().center()
483 self._filing.center().action(),
484 selectionIndex == PieFiling.SELECTION_CENTER,
489 class QPieDisplay(QtGui.QWidget):
491 def __init__(self, filing, parent = None, flags = QtCore.Qt.Window):
492 QtGui.QWidget.__init__(self, parent, flags)
493 self._filing = filing
494 self._artist = PieArtist(self._filing)
495 self._selectionIndex = PieFiling.SELECTION_NONE
497 def popup(self, pos):
498 self._update_selection(pos)
502 return self._artist.pieSize()
504 @misc_utils.log_exception(_moduleLogger)
505 def showEvent(self, showEvent):
506 mask = self._artist.show(self.palette())
509 QtGui.QWidget.showEvent(self, showEvent)
511 @misc_utils.log_exception(_moduleLogger)
512 def hideEvent(self, hideEvent):
514 self._selectionIndex = PieFiling.SELECTION_NONE
515 QtGui.QWidget.hideEvent(self, hideEvent)
517 @misc_utils.log_exception(_moduleLogger)
518 def paintEvent(self, paintEvent):
519 canvas = self._artist.paint(self._selectionIndex)
521 screen = QtGui.QPainter(self)
522 screen.drawPixmap(QtCore.QPoint(0, 0), canvas)
524 QtGui.QWidget.paintEvent(self, paintEvent)
526 def selectAt(self, index):
527 oldIndex = self._selectionIndex
528 self._selectionIndex = index
533 class QPieButton(QtGui.QWidget):
535 activated = QtCore.pyqtSignal(int)
536 highlighted = QtCore.pyqtSignal(int)
537 canceled = QtCore.pyqtSignal()
538 aboutToShow = QtCore.pyqtSignal()
539 aboutToHide = QtCore.pyqtSignal()
544 def __init__(self, buttonSlice, parent = None):
545 # @bug Artifacts on Maemo 5 due to window 3D effects, find way to disable them for just these?
546 # @bug The pie's are being pushed back on screen on Maemo, leading to coordinate issues
547 QtGui.QWidget.__init__(self, parent)
548 self._cachedCenterPosition = self.rect().center()
550 self._filing = PieFiling()
551 self._display = QPieDisplay(self._filing, None, QtCore.Qt.SplashScreen)
552 self._selectionIndex = PieFiling.SELECTION_NONE
554 self._buttonFiling = PieFiling()
555 self._buttonFiling.set_center(buttonSlice)
556 self._buttonFiling.setOuterRadius(self.BUTTON_RADIUS)
557 self._buttonArtist = PieArtist(self._buttonFiling)
558 self._poppedUp = False
560 self._delayPopupTimer = QtCore.QTimer()
561 self._delayPopupTimer.setInterval(self.DELAY)
562 self._delayPopupTimer.setSingleShot(True)
563 self._delayPopupTimer.timeout.connect(self._on_delayed_popup)
564 self._popupLocation = None
566 self._mousePosition = None
567 self.setFocusPolicy(QtCore.Qt.StrongFocus)
569 def insertItem(self, item, index = -1):
570 self._filing.insertItem(item, index)
572 def removeItemAt(self, index):
573 self._filing.removeItemAt(index)
575 def set_center(self, item):
576 self._filing.set_center(item)
578 def set_button(self, item):
584 def itemAt(self, index):
585 return self._filing.itemAt(index)
587 def indexAt(self, point):
588 return self._filing.indexAt(self._cachedCenterPosition, point)
590 def innerRadius(self):
591 return self._filing.innerRadius()
593 def setInnerRadius(self, radius):
594 self._filing.setInnerRadius(radius)
596 def outerRadius(self):
597 return self._filing.outerRadius()
599 def setOuterRadius(self, radius):
600 self._filing.setOuterRadius(radius)
602 def buttonRadius(self):
603 return self._buttonFiling.outerRadius()
605 def setButtonRadius(self, radius):
606 self._buttonFiling.setOuterRadius(radius)
607 self._buttonArtist.show(self.palette())
609 def minimumSizeHint(self):
610 return self._buttonArtist.centerSize()
612 @misc_utils.log_exception(_moduleLogger)
613 def mousePressEvent(self, mouseEvent):
614 lastSelection = self._selectionIndex
616 lastMousePos = mouseEvent.pos()
617 self._mousePosition = lastMousePos
618 self._update_selection(self._cachedCenterPosition)
620 self.highlighted.emit(self._selectionIndex)
622 self._display.selectAt(self._selectionIndex)
623 self._popupLocation = mouseEvent.globalPos()
624 self._delayPopupTimer.start()
626 @misc_utils.log_exception(_moduleLogger)
627 def _on_delayed_popup(self):
628 assert self._popupLocation is not None
629 self._popup_child(self._popupLocation)
631 @misc_utils.log_exception(_moduleLogger)
632 def mouseMoveEvent(self, mouseEvent):
633 lastSelection = self._selectionIndex
635 lastMousePos = mouseEvent.pos()
636 if self._mousePosition is None:
638 self._update_selection(lastMousePos)
641 self._update_selection(
642 self._cachedCenterPosition + (lastMousePos - self._mousePosition),
646 if lastSelection != self._selectionIndex:
647 self.highlighted.emit(self._selectionIndex)
648 self._display.selectAt(self._selectionIndex)
650 if self._selectionIndex != PieFiling.SELECTION_CENTER and self._delayPopupTimer.isActive():
651 self._on_delayed_popup()
653 @misc_utils.log_exception(_moduleLogger)
654 def mouseReleaseEvent(self, mouseEvent):
655 self._delayPopupTimer.stop()
656 self._popupLocation = None
658 lastSelection = self._selectionIndex
660 lastMousePos = mouseEvent.pos()
661 if self._mousePosition is None:
663 self._update_selection(lastMousePos)
666 self._update_selection(
667 self._cachedCenterPosition + (lastMousePos - self._mousePosition),
670 self._mousePosition = None
672 self._activate_at(self._selectionIndex)
675 @misc_utils.log_exception(_moduleLogger)
676 def keyPressEvent(self, keyEvent):
677 if keyEvent.key() in [QtCore.Qt.Key_Right, QtCore.Qt.Key_Down, QtCore.Qt.Key_Tab]:
678 self._popup_child(QtGui.QCursor.pos())
679 if self._selectionIndex != len(self._filing) - 1:
680 nextSelection = self._selectionIndex + 1
683 self._select_at(nextSelection)
684 self._display.selectAt(self._selectionIndex)
685 elif keyEvent.key() in [QtCore.Qt.Key_Left, QtCore.Qt.Key_Up, QtCore.Qt.Key_Backtab]:
686 self._popup_child(QtGui.QCursor.pos())
687 if 0 < self._selectionIndex:
688 nextSelection = self._selectionIndex - 1
690 nextSelection = len(self._filing) - 1
691 self._select_at(nextSelection)
692 self._display.selectAt(self._selectionIndex)
693 elif keyEvent.key() in [QtCore.Qt.Key_Space]:
694 self._popup_child(QtGui.QCursor.pos())
695 self._select_at(PieFiling.SELECTION_CENTER)
696 self._display.selectAt(self._selectionIndex)
697 elif keyEvent.key() in [QtCore.Qt.Key_Return, QtCore.Qt.Key_Enter, QtCore.Qt.Key_Space]:
698 self._delayPopupTimer.stop()
699 self._popupLocation = None
700 self._activate_at(self._selectionIndex)
702 elif keyEvent.key() in [QtCore.Qt.Key_Escape, QtCore.Qt.Key_Backspace]:
703 self._delayPopupTimer.stop()
704 self._popupLocation = None
705 self._activate_at(PieFiling.SELECTION_NONE)
708 QtGui.QWidget.keyPressEvent(self, keyEvent)
710 @misc_utils.log_exception(_moduleLogger)
711 def resizeEvent(self, resizeEvent):
712 self.setButtonRadius(min(resizeEvent.size().width(), resizeEvent.size().height()) / 2 - 1)
713 QtGui.QWidget.resizeEvent(self, resizeEvent)
715 @misc_utils.log_exception(_moduleLogger)
716 def showEvent(self, showEvent):
717 self._buttonArtist.show(self.palette())
718 self._cachedCenterPosition = self.rect().center()
720 QtGui.QWidget.showEvent(self, showEvent)
722 @misc_utils.log_exception(_moduleLogger)
723 def hideEvent(self, hideEvent):
725 self._select_at(PieFiling.SELECTION_NONE)
726 QtGui.QWidget.hideEvent(self, hideEvent)
728 @misc_utils.log_exception(_moduleLogger)
729 def paintEvent(self, paintEvent):
730 self.setButtonRadius(min(self.rect().width(), self.rect().height()) / 2 - 1)
732 canvas = self._buttonArtist.paint(PieFiling.SELECTION_CENTER)
734 canvas = self._buttonArtist.paint(PieFiling.SELECTION_NONE)
736 screen = QtGui.QPainter(self)
737 screen.drawPixmap(QtCore.QPoint(0, 0), canvas)
739 QtGui.QWidget.paintEvent(self, paintEvent)
742 return iter(self._filing)
745 return len(self._filing)
747 def _popup_child(self, position):
748 self._poppedUp = True
749 self.aboutToShow.emit()
751 self._delayPopupTimer.stop()
752 self._popupLocation = None
754 position = position - QtCore.QPoint(self._filing.outerRadius(), self._filing.outerRadius())
755 self._display.move(position)
760 def _hide_child(self):
761 self._poppedUp = False
762 self.aboutToHide.emit()
766 def _select_at(self, index):
767 self._selectionIndex = index
769 def _update_selection(self, lastMousePos, ignoreOuter = False):
770 radius = _radius_at(self._cachedCenterPosition, lastMousePos)
771 if radius < self._filing.innerRadius():
772 self._select_at(PieFiling.SELECTION_CENTER)
773 elif radius <= self._filing.outerRadius() or ignoreOuter:
774 self._select_at(self.indexAt(lastMousePos))
776 self._select_at(PieFiling.SELECTION_NONE)
778 def _activate_at(self, index):
779 if index == PieFiling.SELECTION_NONE:
782 elif index == PieFiling.SELECTION_CENTER:
783 child = self._filing.center()
785 child = self.itemAt(index)
787 if child.action().isEnabled():
788 child.action().trigger()
789 self.activated.emit(index)
794 class QPieMenu(QtGui.QWidget):
796 activated = QtCore.pyqtSignal(int)
797 highlighted = QtCore.pyqtSignal(int)
798 canceled = QtCore.pyqtSignal()
799 aboutToShow = QtCore.pyqtSignal()
800 aboutToHide = QtCore.pyqtSignal()
802 def __init__(self, parent = None):
803 QtGui.QWidget.__init__(self, parent)
804 self._cachedCenterPosition = self.rect().center()
806 self._filing = PieFiling()
807 self._artist = PieArtist(self._filing)
808 self._selectionIndex = PieFiling.SELECTION_NONE
810 self._mousePosition = ()
811 self.setFocusPolicy(QtCore.Qt.StrongFocus)
813 def popup(self, pos):
814 self._update_selection(pos)
817 def insertItem(self, item, index = -1):
818 self._filing.insertItem(item, index)
821 def removeItemAt(self, index):
822 self._filing.removeItemAt(index)
825 def set_center(self, item):
826 self._filing.set_center(item)
833 def itemAt(self, index):
834 return self._filing.itemAt(index)
836 def indexAt(self, point):
837 return self._filing.indexAt(self._cachedCenterPosition, point)
839 def innerRadius(self):
840 return self._filing.innerRadius()
842 def setInnerRadius(self, radius):
843 self._filing.setInnerRadius(radius)
846 def outerRadius(self):
847 return self._filing.outerRadius()
849 def setOuterRadius(self, radius):
850 self._filing.setOuterRadius(radius)
854 return self._artist.pieSize()
856 @misc_utils.log_exception(_moduleLogger)
857 def mousePressEvent(self, mouseEvent):
858 lastSelection = self._selectionIndex
860 lastMousePos = mouseEvent.pos()
861 self._update_selection(lastMousePos)
862 self._mousePosition = lastMousePos
864 if lastSelection != self._selectionIndex:
865 self.highlighted.emit(self._selectionIndex)
868 @misc_utils.log_exception(_moduleLogger)
869 def mouseMoveEvent(self, mouseEvent):
870 lastSelection = self._selectionIndex
872 lastMousePos = mouseEvent.pos()
873 self._update_selection(lastMousePos)
875 if lastSelection != self._selectionIndex:
876 self.highlighted.emit(self._selectionIndex)
879 @misc_utils.log_exception(_moduleLogger)
880 def mouseReleaseEvent(self, mouseEvent):
881 lastSelection = self._selectionIndex
883 lastMousePos = mouseEvent.pos()
884 self._update_selection(lastMousePos)
885 self._mousePosition = ()
887 self._activate_at(self._selectionIndex)
890 @misc_utils.log_exception(_moduleLogger)
891 def keyPressEvent(self, keyEvent):
892 if keyEvent.key() in [QtCore.Qt.Key_Right, QtCore.Qt.Key_Down, QtCore.Qt.Key_Tab]:
893 if self._selectionIndex != len(self._filing) - 1:
894 nextSelection = self._selectionIndex + 1
897 self._select_at(nextSelection)
899 elif keyEvent.key() in [QtCore.Qt.Key_Left, QtCore.Qt.Key_Up, QtCore.Qt.Key_Backtab]:
900 if 0 < self._selectionIndex:
901 nextSelection = self._selectionIndex - 1
903 nextSelection = len(self._filing) - 1
904 self._select_at(nextSelection)
906 elif keyEvent.key() in [QtCore.Qt.Key_Return, QtCore.Qt.Key_Enter, QtCore.Qt.Key_Space]:
907 self._activate_at(self._selectionIndex)
908 elif keyEvent.key() in [QtCore.Qt.Key_Escape, QtCore.Qt.Key_Backspace]:
909 self._activate_at(PieFiling.SELECTION_NONE)
911 QtGui.QWidget.keyPressEvent(self, keyEvent)
913 @misc_utils.log_exception(_moduleLogger)
914 def showEvent(self, showEvent):
915 self.aboutToShow.emit()
916 self._cachedCenterPosition = self.rect().center()
918 mask = self._artist.show(self.palette())
921 lastMousePos = self.mapFromGlobal(QtGui.QCursor.pos())
922 self._update_selection(lastMousePos)
924 QtGui.QWidget.showEvent(self, showEvent)
926 @misc_utils.log_exception(_moduleLogger)
927 def hideEvent(self, hideEvent):
929 self._selectionIndex = PieFiling.SELECTION_NONE
930 QtGui.QWidget.hideEvent(self, hideEvent)
932 @misc_utils.log_exception(_moduleLogger)
933 def paintEvent(self, paintEvent):
934 canvas = self._artist.paint(self._selectionIndex)
936 screen = QtGui.QPainter(self)
937 screen.drawPixmap(QtCore.QPoint(0, 0), canvas)
939 QtGui.QWidget.paintEvent(self, paintEvent)
942 return iter(self._filing)
945 return len(self._filing)
947 def _select_at(self, index):
948 self._selectionIndex = index
950 def _update_selection(self, lastMousePos):
951 radius = _radius_at(self._cachedCenterPosition, lastMousePos)
952 if radius < self._filing.innerRadius():
953 self._selectionIndex = PieFiling.SELECTION_CENTER
954 elif radius <= self._filing.outerRadius():
955 self._select_at(self.indexAt(lastMousePos))
957 self._selectionIndex = PieFiling.SELECTION_NONE
959 def _activate_at(self, index):
960 if index == PieFiling.SELECTION_NONE:
962 self.aboutToHide.emit()
965 elif index == PieFiling.SELECTION_CENTER:
966 child = self._filing.center()
968 child = self.itemAt(index)
970 if child.isEnabled():
971 child.action().trigger()
972 self.activated.emit(index)
975 self.aboutToHide.emit()
980 PieFiling.NULL_CENTER.setEnabled(False)
987 def _on_about_to_hide(app):
991 if __name__ == "__main__":
992 app = QtGui.QApplication([])
1000 singleAction = QtGui.QAction(None)
1001 singleAction.setText("Boo")
1002 singleItem = QActionPieItem(singleAction)
1004 spie.insertItem(singleItem)
1008 oneAction = QtGui.QAction(None)
1009 oneAction.setText("Chew")
1010 oneItem = QActionPieItem(oneAction)
1011 twoAction = QtGui.QAction(None)
1012 twoAction.setText("Foo")
1013 twoItem = QActionPieItem(twoAction)
1014 iconTextAction = QtGui.QAction(None)
1015 iconTextAction.setText("Icon")
1016 iconTextAction.setIcon(QtGui.QIcon.fromTheme("gtk-close"))
1017 iconTextItem = QActionPieItem(iconTextAction)
1019 mpie.insertItem(oneItem)
1020 mpie.insertItem(twoItem)
1021 mpie.insertItem(oneItem)
1022 mpie.insertItem(iconTextItem)
1026 oneAction = QtGui.QAction(None)
1027 oneAction.setText("Chew")
1028 oneAction.triggered.connect(lambda: _print("Chew"))
1029 oneItem = QActionPieItem(oneAction)
1030 twoAction = QtGui.QAction(None)
1031 twoAction.setText("Foo")
1032 twoAction.triggered.connect(lambda: _print("Foo"))
1033 twoItem = QActionPieItem(twoAction)
1034 iconAction = QtGui.QAction(None)
1035 iconAction.setIcon(QtGui.QIcon.fromTheme("gtk-open"))
1036 iconAction.triggered.connect(lambda: _print("Icon"))
1037 iconItem = QActionPieItem(iconAction)
1038 iconTextAction = QtGui.QAction(None)
1039 iconTextAction.setText("Icon")
1040 iconTextAction.setIcon(QtGui.QIcon.fromTheme("gtk-close"))
1041 iconTextAction.triggered.connect(lambda: _print("Icon and text"))
1042 iconTextItem = QActionPieItem(iconTextAction)
1044 mpie.set_center(iconItem)
1045 mpie.insertItem(oneItem)
1046 mpie.insertItem(twoItem)
1047 mpie.insertItem(oneItem)
1048 mpie.insertItem(iconTextItem)
1050 mpie.aboutToHide.connect(lambda: _on_about_to_hide(app))
1051 mpie.canceled.connect(lambda: _print("Canceled"))
1054 oneAction = QtGui.QAction(None)
1055 oneAction.setText("Chew")
1056 oneAction.triggered.connect(lambda: _print("Chew"))
1057 oneItem = QActionPieItem(oneAction)
1058 twoAction = QtGui.QAction(None)
1059 twoAction.setText("Foo")
1060 twoAction.triggered.connect(lambda: _print("Foo"))
1061 twoItem = QActionPieItem(twoAction)
1062 iconAction = QtGui.QAction(None)
1063 iconAction.setIcon(QtGui.QIcon.fromTheme("gtk-open"))
1064 iconAction.triggered.connect(lambda: _print("Icon"))
1065 iconItem = QActionPieItem(iconAction)
1066 iconTextAction = QtGui.QAction(None)
1067 iconTextAction.setText("Icon")
1068 iconTextAction.setIcon(QtGui.QIcon.fromTheme("gtk-close"))
1069 iconTextAction.triggered.connect(lambda: _print("Icon and text"))
1070 iconTextItem = QActionPieItem(iconTextAction)
1071 pieFiling = PieFiling()
1072 pieFiling.set_center(iconItem)
1073 pieFiling.insertItem(oneItem)
1074 pieFiling.insertItem(twoItem)
1075 pieFiling.insertItem(oneItem)
1076 pieFiling.insertItem(iconTextItem)
1077 mpie = QPieDisplay(pieFiling)
1081 oneAction = QtGui.QAction(None)
1082 oneAction.setText("Chew")
1083 oneAction.triggered.connect(lambda: _print("Chew"))
1084 oneItem = QActionPieItem(oneAction)
1085 twoAction = QtGui.QAction(None)
1086 twoAction.setText("Foo")
1087 twoAction.triggered.connect(lambda: _print("Foo"))
1088 twoItem = QActionPieItem(twoAction)
1089 iconAction = QtGui.QAction(None)
1090 iconAction.setIcon(QtGui.QIcon.fromTheme("gtk-open"))
1091 iconAction.triggered.connect(lambda: _print("Icon"))
1092 iconItem = QActionPieItem(iconAction)
1093 iconTextAction = QtGui.QAction(None)
1094 iconTextAction.setText("Icon")
1095 iconTextAction.setIcon(QtGui.QIcon.fromTheme("gtk-close"))
1096 iconTextAction.triggered.connect(lambda: _print("Icon and text"))
1097 iconTextItem = QActionPieItem(iconTextAction)
1098 mpie = QPieButton(iconItem)
1099 mpie.set_center(iconItem)
1100 mpie.insertItem(oneItem)
1101 mpie.insertItem(twoItem)
1102 mpie.insertItem(oneItem)
1103 mpie.insertItem(iconTextItem)
1105 mpie.aboutToHide.connect(lambda: _on_about_to_hide(app))
1106 mpie.canceled.connect(lambda: _print("Canceled"))