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 def _radius_at(center, pos):
33 radius = math.sqrt(xDelta ** 2 + yDelta ** 2)
37 def _angle_at(center, pos):
42 radius = math.sqrt(xDelta ** 2 + yDelta ** 2)
43 angle = math.acos(xDelta / radius)
45 angle = _TWOPI - angle
50 class QActionPieItem(object):
52 def __init__(self, action, weight = 1):
59 def setWeight(self, weight):
65 def setEnabled(self, enabled = True):
66 self._action.setEnabled(enabled)
69 return self._action.isEnabled()
72 class PieFiling(object):
74 INNER_RADIUS_DEFAULT = 32
75 OUTER_RADIUS_DEFAULT = 128
80 NULL_CENTER = QActionPieItem(QtGui.QAction(None))
83 self._innerRadius = self.INNER_RADIUS_DEFAULT
84 self._outerRadius = self.OUTER_RADIUS_DEFAULT
86 self._center = self.NULL_CENTER
88 self._cacheIndexToAngle = {}
89 self._cacheTotalWeight = 0
91 def insertItem(self, item, index = -1):
92 self._children.insert(index, item)
93 self._invalidate_cache()
95 def removeItemAt(self, index):
96 item = self._children.pop(index)
97 self._invalidate_cache()
99 def set_center(self, item):
101 item = self.NULL_CENTER
108 del self._children[:]
109 self._center = self.NULL_CENTER
110 self._invalidate_cache()
112 def itemAt(self, index):
113 return self._children[index]
115 def indexAt(self, center, point):
116 return self._angle_to_index(_angle_at(center, point))
118 def innerRadius(self):
119 return self._innerRadius
121 def setInnerRadius(self, radius):
122 self._innerRadius = radius
124 def outerRadius(self):
125 return self._outerRadius
127 def setOuterRadius(self, radius):
128 self._outerRadius = radius
131 return iter(self._children)
134 return len(self._children)
136 def __getitem__(self, index):
137 return self._children[index]
139 def _invalidate_cache(self):
140 self._cacheIndexToAngle.clear()
141 self._cacheTotalWeight = sum(child.weight() for child in self._children)
142 if self._cacheTotalWeight == 0:
143 self._cacheTotalWeight = 1
145 def _index_to_angle(self, index, isShifted):
146 key = index, isShifted
147 if key in self._cacheIndexToAngle:
148 return self._cacheIndexToAngle[key]
149 index = index % len(self._children)
151 baseAngle = _TWOPI / self._cacheTotalWeight
156 angle -= (self._children[0].weight() * baseAngle) / 2
158 angle -= baseAngle / 2
162 for i, child in enumerate(self._children):
165 angle += child.weight() * baseAngle
166 while _TWOPI < angle:
169 self._cacheIndexToAngle[key] = angle
172 def _angle_to_index(self, angle):
173 numChildren = len(self._children)
175 return self.SELECTION_CENTER
177 baseAngle = _TWOPI / self._cacheTotalWeight
179 iterAngle = math.pi / 2 - (self.itemAt(0).weight() * baseAngle) / 2
183 oldIterAngle = iterAngle
184 for index, child in enumerate(self._children):
185 iterAngle += child.weight() * baseAngle
186 if oldIterAngle < angle and angle <= iterAngle:
187 return index - 1 if index != 0 else numChildren - 1
188 elif oldIterAngle < (angle + _TWOPI) and (angle + _TWOPI <= iterAngle):
189 return index - 1 if index != 0 else numChildren - 1
190 oldIterAngle = iterAngle
193 class PieArtist(object):
195 ICON_SIZE_DEFAULT = 32
197 def __init__(self, filing):
198 # @bug The wrong text is being using on unselected pie menus
199 # @bug Plus it would be a good idea to fill in the icon body's
200 # @bug Maybe even make the icons glow?
201 self._filing = filing
203 self._cachedOuterRadius = self._filing.outerRadius()
204 self._cachedInnerRadius = self._filing.innerRadius()
205 canvasSize = self._cachedOuterRadius * 2 + 1
206 self._canvas = QtGui.QPixmap(canvasSize, canvasSize)
211 diameter = self._filing.outerRadius() * 2 + 1
212 return QtCore.QSize(diameter, diameter)
214 def centerSize(self):
215 painter = QtGui.QPainter(self._canvas)
216 text = self._filing.center().action().text()
217 fontMetrics = painter.fontMetrics()
219 textBoundingRect = fontMetrics.boundingRect(text)
221 textBoundingRect = QtCore.QRect()
222 textWidth = textBoundingRect.width()
223 textHeight = textBoundingRect.height()
226 textWidth + self.ICON_SIZE_DEFAULT,
227 max(textHeight, self.ICON_SIZE_DEFAULT),
230 def show(self, palette):
231 self.palette = palette
234 self._cachedOuterRadius != self._filing.outerRadius() or
235 self._cachedInnerRadius != self._filing.innerRadius()
237 self._cachedOuterRadius = self._filing.outerRadius()
238 self._cachedInnerRadius = self._filing.innerRadius()
239 self._canvas = self._canvas.scaled(self.pieSize())
241 if self._mask is None:
242 self._mask = QtGui.QBitmap(self._canvas.size())
243 self._mask.fill(QtCore.Qt.color0)
244 self._generate_mask(self._mask)
245 self._canvas.setMask(self._mask)
251 def paint(self, selectionIndex):
252 painter = QtGui.QPainter(self._canvas)
253 painter.setRenderHint(QtGui.QPainter.Antialiasing, True)
255 adjustmentRect = self._canvas.rect().adjusted(0, 0, -1, -1)
257 numChildren = len(self._filing)
259 if selectionIndex == PieFiling.SELECTION_CENTER and self._filing.center().isEnabled():
260 painter.setBrush(self.palette.highlight())
262 painter.setBrush(self.palette.background())
263 painter.setPen(self.palette.mid().color())
265 painter.drawRect(self._canvas.rect())
266 self._paint_center_foreground(painter, selectionIndex)
268 elif numChildren == 1:
269 if selectionIndex == 0 and self._filing[0].isEnabled():
270 painter.setBrush(self.palette.highlight())
272 painter.setBrush(self.palette.background())
274 painter.fillRect(self._canvas.rect(), painter.brush())
276 for i in xrange(len(self._filing)):
277 self._paint_slice_background(painter, adjustmentRect, i, selectionIndex)
279 self._paint_center_background(painter, adjustmentRect, selectionIndex)
280 self._paint_center_foreground(painter, selectionIndex)
282 for i in xrange(len(self._filing)):
283 self._paint_slice_foreground(painter, i, selectionIndex)
287 def _generate_mask(self, mask):
289 Specifies on the mask the shape of the pie menu
291 painter = QtGui.QPainter(mask)
292 painter.setPen(QtCore.Qt.color1)
293 painter.setBrush(QtCore.Qt.color1)
294 painter.drawEllipse(mask.rect().adjusted(0, 0, -1, -1))
296 def _paint_slice_background(self, painter, adjustmentRect, i, selectionIndex):
297 if i == selectionIndex and self._filing[i].isEnabled():
298 painter.setBrush(self.palette.highlight())
300 painter.setBrush(self.palette.background())
301 painter.setPen(self.palette.mid().color())
303 a = self._filing._index_to_angle(i, True)
304 b = self._filing._index_to_angle(i + 1, True)
311 startAngleInDeg = (a * 360 * 16) / _TWOPI
312 sizeInDeg = (size * 360 * 16) / _TWOPI
313 painter.drawPie(adjustmentRect, int(startAngleInDeg), int(sizeInDeg))
315 def _paint_slice_foreground(self, painter, i, selectionIndex):
316 child = self._filing[i]
318 a = self._filing._index_to_angle(i, True)
319 b = self._filing._index_to_angle(i + 1, True)
322 middleAngle = (a + b) / 2
323 averageRadius = (self._cachedInnerRadius + self._cachedOuterRadius) / 2
325 sliceX = averageRadius * math.cos(middleAngle)
326 sliceY = - averageRadius * math.sin(middleAngle)
328 piePos = self._canvas.rect().center()
332 painter, child.action(), i == selectionIndex, pieX+sliceX, pieY+sliceY
335 def _paint_label(self, painter, action, isSelected, x, y):
337 fontMetrics = painter.fontMetrics()
339 textBoundingRect = fontMetrics.boundingRect(text)
341 textBoundingRect = QtCore.QRect()
342 textWidth = textBoundingRect.width()
343 textHeight = textBoundingRect.height()
345 icon = action.icon().pixmap(
346 QtCore.QSize(self.ICON_SIZE_DEFAULT, self.ICON_SIZE_DEFAULT),
350 iconWidth = icon.width()
351 iconHeight = icon.width()
352 averageWidth = (iconWidth + textWidth)/2
353 if not icon.isNull():
354 iconRect = QtCore.QRect(
361 painter.drawPixmap(iconRect, icon)
365 if action.isEnabled():
366 pen = self.palette.highlightedText()
367 brush = self.palette.highlight()
369 pen = self.palette.mid()
370 brush = self.palette.background()
372 if action.isEnabled():
373 pen = self.palette.text()
375 pen = self.palette.mid()
376 brush = self.palette.background()
378 leftX = x - averageWidth + iconWidth
379 topY = y + textHeight/2
380 painter.setPen(pen.color())
381 painter.setBrush(brush)
382 painter.drawText(leftX, topY, text)
384 def _paint_center_background(self, painter, adjustmentRect, selectionIndex):
385 dark = self.palette.dark().color()
386 light = self.palette.light().color()
387 if selectionIndex == PieFiling.SELECTION_CENTER and self._filing.center().isEnabled():
388 background = self.palette.highlight().color()
390 background = self.palette.background().color()
392 innerRadius = self._cachedInnerRadius
393 adjustmentCenterPos = adjustmentRect.center()
394 innerRect = QtCore.QRect(
395 adjustmentCenterPos.x() - innerRadius,
396 adjustmentCenterPos.y() - innerRadius,
401 painter.setPen(QtCore.Qt.NoPen)
402 painter.setBrush(background)
403 painter.drawPie(innerRect, 0, 360 * 16)
405 painter.setPen(QtGui.QPen(dark, 1))
406 painter.setBrush(QtCore.Qt.NoBrush)
407 painter.drawEllipse(innerRect)
409 painter.setPen(QtGui.QPen(dark, 1))
410 painter.setBrush(QtCore.Qt.NoBrush)
411 painter.drawEllipse(adjustmentRect)
413 r = QtCore.QRect(innerRect)
414 innerCenter = r.center()
415 innerRect.setLeft(innerCenter.x() + ((r.left() - innerCenter.x()) / 3) * 1)
416 innerRect.setRight(innerCenter.x() + ((r.right() - innerCenter.x()) / 3) * 1)
417 innerRect.setTop(innerCenter.y() + ((r.top() - innerCenter.y()) / 3) * 1)
418 innerRect.setBottom(innerCenter.y() + ((r.bottom() - innerCenter.y()) / 3) * 1)
420 def _paint_center_foreground(self, painter, selectionIndex):
421 centerPos = self._canvas.rect().center()
430 self._filing.center().action(),
431 selectionIndex == PieFiling.SELECTION_CENTER,
436 class QPieDisplay(QtGui.QWidget):
438 def __init__(self, filing, parent = None, flags = QtCore.Qt.Window):
439 QtGui.QWidget.__init__(self, parent, flags)
440 self._filing = filing
441 self._artist = PieArtist(self._filing)
442 self._selectionIndex = PieFiling.SELECTION_NONE
444 def popup(self, pos):
445 self._update_selection(pos)
449 return self._artist.pieSize()
451 @misc_utils.log_exception(_moduleLogger)
452 def showEvent(self, showEvent):
453 mask = self._artist.show(self.palette())
456 QtGui.QWidget.showEvent(self, showEvent)
458 @misc_utils.log_exception(_moduleLogger)
459 def hideEvent(self, hideEvent):
461 self._selectionIndex = PieFiling.SELECTION_NONE
462 QtGui.QWidget.hideEvent(self, hideEvent)
464 @misc_utils.log_exception(_moduleLogger)
465 def paintEvent(self, paintEvent):
466 canvas = self._artist.paint(self._selectionIndex)
468 screen = QtGui.QPainter(self)
469 screen.drawPixmap(QtCore.QPoint(0, 0), canvas)
471 QtGui.QWidget.paintEvent(self, paintEvent)
473 def selectAt(self, index):
474 oldIndex = self._selectionIndex
475 self._selectionIndex = index
480 class QPieButton(QtGui.QWidget):
482 activated = QtCore.pyqtSignal(int)
483 highlighted = QtCore.pyqtSignal(int)
484 canceled = QtCore.pyqtSignal()
485 aboutToShow = QtCore.pyqtSignal()
486 aboutToHide = QtCore.pyqtSignal()
491 def __init__(self, buttonSlice, parent = None):
492 QtGui.QWidget.__init__(self, parent)
493 self._cachedCenterPosition = self.rect().center()
495 self._filing = PieFiling()
496 self._display = QPieDisplay(self._filing, None, QtCore.Qt.SplashScreen)
497 self._selectionIndex = PieFiling.SELECTION_NONE
499 self._buttonFiling = PieFiling()
500 self._buttonFiling.set_center(buttonSlice)
501 # @todo Figure out how to make the button auto-fill to content
502 self._buttonFiling.setOuterRadius(self.BUTTON_RADIUS)
503 self._buttonArtist = PieArtist(self._buttonFiling)
504 self._poppedUp = False
506 self._delayPopupTimer = QtCore.QTimer()
507 self._delayPopupTimer.setInterval(self.DELAY)
508 self._delayPopupTimer.setSingleShot(True)
509 self._delayPopupTimer.timeout.connect(self._on_delayed_popup)
510 self._popupLocation = None
512 self._mousePosition = None
513 self.setFocusPolicy(QtCore.Qt.StrongFocus)
515 def insertItem(self, item, index = -1):
516 self._filing.insertItem(item, index)
518 def removeItemAt(self, index):
519 self._filing.removeItemAt(index)
521 def set_center(self, item):
522 self._filing.set_center(item)
524 def set_button(self, item):
530 def itemAt(self, index):
531 return self._filing.itemAt(index)
533 def indexAt(self, point):
534 return self._filing.indexAt(self._cachedCenterPosition, point)
536 def innerRadius(self):
537 return self._filing.innerRadius()
539 def setInnerRadius(self, radius):
540 self._filing.setInnerRadius(radius)
542 def outerRadius(self):
543 return self._filing.outerRadius()
545 def setOuterRadius(self, radius):
546 self._filing.setOuterRadius(radius)
548 def buttonRadius(self):
549 return self._buttonFiling.outerRadius()
551 def setButtonRadius(self, radius):
552 self._buttonFiling.setOuterRadius(radius)
555 return self._buttonArtist.pieSize()
557 @misc_utils.log_exception(_moduleLogger)
558 def mousePressEvent(self, mouseEvent):
559 lastSelection = self._selectionIndex
561 lastMousePos = mouseEvent.pos()
562 self._mousePosition = lastMousePos
563 self._update_selection(self._cachedCenterPosition)
565 self.highlighted.emit(self._selectionIndex)
567 self._display.selectAt(self._selectionIndex)
568 self._popupLocation = mouseEvent.globalPos()
569 self._delayPopupTimer.start()
571 @misc_utils.log_exception(_moduleLogger)
572 def _on_delayed_popup(self):
573 assert self._popupLocation is not None
574 self._popup_child(self._popupLocation)
576 @misc_utils.log_exception(_moduleLogger)
577 def mouseMoveEvent(self, mouseEvent):
578 lastSelection = self._selectionIndex
580 lastMousePos = mouseEvent.pos()
581 if self._mousePosition is None:
583 self._update_selection(lastMousePos)
586 self._update_selection(self._cachedCenterPosition + (lastMousePos - self._mousePosition))
588 if lastSelection != self._selectionIndex:
589 self.highlighted.emit(self._selectionIndex)
590 self._display.selectAt(self._selectionIndex)
592 if self._selectionIndex != PieFiling.SELECTION_CENTER and self._delayPopupTimer.isActive():
593 self._on_delayed_popup()
595 @misc_utils.log_exception(_moduleLogger)
596 def mouseReleaseEvent(self, mouseEvent):
597 self._delayPopupTimer.stop()
598 self._popupLocation = None
600 lastSelection = self._selectionIndex
602 lastMousePos = mouseEvent.pos()
603 if self._mousePosition is None:
605 self._update_selection(lastMousePos)
608 self._update_selection(self._cachedCenterPosition + (lastMousePos - self._mousePosition))
609 self._mousePosition = None
611 self._activate_at(self._selectionIndex)
614 @misc_utils.log_exception(_moduleLogger)
615 def keyPressEvent(self, keyEvent):
616 if keyEvent.key() in [QtCore.Qt.Key_Right, QtCore.Qt.Key_Down, QtCore.Qt.Key_Tab]:
617 self._popup_child(QtGui.QCursor.pos())
618 if self._selectionIndex != len(self._filing) - 1:
619 nextSelection = self._selectionIndex + 1
622 self._select_at(nextSelection)
623 self._display.selectAt(self._selectionIndex)
624 elif keyEvent.key() in [QtCore.Qt.Key_Left, QtCore.Qt.Key_Up, QtCore.Qt.Key_Backtab]:
625 self._popup_child(QtGui.QCursor.pos())
626 if 0 < self._selectionIndex:
627 nextSelection = self._selectionIndex - 1
629 nextSelection = len(self._filing) - 1
630 self._select_at(nextSelection)
631 self._display.selectAt(self._selectionIndex)
632 elif keyEvent.key() in [QtCore.Qt.Key_Space]:
633 self._popup_child(QtGui.QCursor.pos())
634 self._select_at(PieFiling.SELECTION_CENTER)
635 self._display.selectAt(self._selectionIndex)
636 elif keyEvent.key() in [QtCore.Qt.Key_Return, QtCore.Qt.Key_Enter, QtCore.Qt.Key_Space]:
637 self._delayPopupTimer.stop()
638 self._popupLocation = None
639 self._activate_at(self._selectionIndex)
641 elif keyEvent.key() in [QtCore.Qt.Key_Escape, QtCore.Qt.Key_Backspace]:
642 self._delayPopupTimer.stop()
643 self._popupLocation = None
644 self._activate_at(PieFiling.SELECTION_NONE)
647 QtGui.QWidget.keyPressEvent(self, keyEvent)
649 @misc_utils.log_exception(_moduleLogger)
650 def showEvent(self, showEvent):
651 self._buttonArtist.show(self.palette())
652 self._cachedCenterPosition = self.rect().center()
654 QtGui.QWidget.showEvent(self, showEvent)
656 @misc_utils.log_exception(_moduleLogger)
657 def hideEvent(self, hideEvent):
659 self._select_at(PieFiling.SELECTION_NONE)
660 QtGui.QWidget.hideEvent(self, hideEvent)
662 @misc_utils.log_exception(_moduleLogger)
663 def paintEvent(self, paintEvent):
665 canvas = self._buttonArtist.paint(PieFiling.SELECTION_CENTER)
667 canvas = self._buttonArtist.paint(PieFiling.SELECTION_NONE)
669 screen = QtGui.QPainter(self)
670 screen.drawPixmap(QtCore.QPoint(0, 0), canvas)
672 QtGui.QWidget.paintEvent(self, paintEvent)
675 return iter(self._filing)
678 return len(self._filing)
680 def _popup_child(self, position):
681 self._poppedUp = True
682 self.aboutToShow.emit()
684 self._delayPopupTimer.stop()
685 self._popupLocation = None
687 position = position - QtCore.QPoint(self._filing.outerRadius(), self._filing.outerRadius())
688 self._display.move(position)
693 def _hide_child(self):
694 self._poppedUp = False
695 self.aboutToHide.emit()
699 def _select_at(self, index):
700 self._selectionIndex = index
702 def _update_selection(self, lastMousePos):
703 radius = _radius_at(self._cachedCenterPosition, lastMousePos)
704 if radius < self._filing.innerRadius():
705 self._select_at(PieFiling.SELECTION_CENTER)
706 elif radius <= self._filing.outerRadius():
707 self._select_at(self.indexAt(lastMousePos))
709 self._select_at(PieFiling.SELECTION_NONE)
711 def _activate_at(self, index):
712 if index == PieFiling.SELECTION_NONE:
715 elif index == PieFiling.SELECTION_CENTER:
716 child = self._filing.center()
718 child = self.itemAt(index)
720 if child.action().isEnabled():
721 child.action().trigger()
722 self.activated.emit(index)
727 class QPieMenu(QtGui.QWidget):
729 activated = QtCore.pyqtSignal(int)
730 highlighted = QtCore.pyqtSignal(int)
731 canceled = QtCore.pyqtSignal()
732 aboutToShow = QtCore.pyqtSignal()
733 aboutToHide = QtCore.pyqtSignal()
735 def __init__(self, parent = None):
736 QtGui.QWidget.__init__(self, parent)
737 self._cachedCenterPosition = self.rect().center()
739 self._filing = PieFiling()
740 self._artist = PieArtist(self._filing)
741 self._selectionIndex = PieFiling.SELECTION_NONE
743 self._mousePosition = ()
744 self.setFocusPolicy(QtCore.Qt.StrongFocus)
746 def popup(self, pos):
747 self._update_selection(pos)
750 def insertItem(self, item, index = -1):
751 self._filing.insertItem(item, index)
754 def removeItemAt(self, index):
755 self._filing.removeItemAt(index)
758 def set_center(self, item):
759 self._filing.set_center(item)
766 def itemAt(self, index):
767 return self._filing.itemAt(index)
769 def indexAt(self, point):
770 return self._filing.indexAt(self._cachedCenterPosition, point)
772 def innerRadius(self):
773 return self._filing.innerRadius()
775 def setInnerRadius(self, radius):
776 self._filing.setInnerRadius(radius)
779 def outerRadius(self):
780 return self._filing.outerRadius()
782 def setOuterRadius(self, radius):
783 self._filing.setOuterRadius(radius)
787 return self._artist.pieSize()
789 @misc_utils.log_exception(_moduleLogger)
790 def mousePressEvent(self, mouseEvent):
791 lastSelection = self._selectionIndex
793 lastMousePos = mouseEvent.pos()
794 self._update_selection(lastMousePos)
795 self._mousePosition = lastMousePos
797 if lastSelection != self._selectionIndex:
798 self.highlighted.emit(self._selectionIndex)
801 @misc_utils.log_exception(_moduleLogger)
802 def mouseMoveEvent(self, mouseEvent):
803 lastSelection = self._selectionIndex
805 lastMousePos = mouseEvent.pos()
806 self._update_selection(lastMousePos)
808 if lastSelection != self._selectionIndex:
809 self.highlighted.emit(self._selectionIndex)
812 @misc_utils.log_exception(_moduleLogger)
813 def mouseReleaseEvent(self, mouseEvent):
814 lastSelection = self._selectionIndex
816 lastMousePos = mouseEvent.pos()
817 self._update_selection(lastMousePos)
818 self._mousePosition = ()
820 self._activate_at(self._selectionIndex)
823 @misc_utils.log_exception(_moduleLogger)
824 def keyPressEvent(self, keyEvent):
825 if keyEvent.key() in [QtCore.Qt.Key_Right, QtCore.Qt.Key_Down, QtCore.Qt.Key_Tab]:
826 if self._selectionIndex != len(self._filing) - 1:
827 nextSelection = self._selectionIndex + 1
830 self._select_at(nextSelection)
832 elif keyEvent.key() in [QtCore.Qt.Key_Left, QtCore.Qt.Key_Up, QtCore.Qt.Key_Backtab]:
833 if 0 < self._selectionIndex:
834 nextSelection = self._selectionIndex - 1
836 nextSelection = len(self._filing) - 1
837 self._select_at(nextSelection)
839 elif keyEvent.key() in [QtCore.Qt.Key_Return, QtCore.Qt.Key_Enter, QtCore.Qt.Key_Space]:
840 self._activate_at(self._selectionIndex)
841 elif keyEvent.key() in [QtCore.Qt.Key_Escape, QtCore.Qt.Key_Backspace]:
842 self._activate_at(PieFiling.SELECTION_NONE)
844 QtGui.QWidget.keyPressEvent(self, keyEvent)
846 @misc_utils.log_exception(_moduleLogger)
847 def showEvent(self, showEvent):
848 self.aboutToShow.emit()
849 self._cachedCenterPosition = self.rect().center()
851 mask = self._artist.show(self.palette())
854 lastMousePos = self.mapFromGlobal(QtGui.QCursor.pos())
855 self._update_selection(lastMousePos)
857 QtGui.QWidget.showEvent(self, showEvent)
859 @misc_utils.log_exception(_moduleLogger)
860 def hideEvent(self, hideEvent):
862 self._selectionIndex = PieFiling.SELECTION_NONE
863 QtGui.QWidget.hideEvent(self, hideEvent)
865 @misc_utils.log_exception(_moduleLogger)
866 def paintEvent(self, paintEvent):
867 canvas = self._artist.paint(self._selectionIndex)
869 screen = QtGui.QPainter(self)
870 screen.drawPixmap(QtCore.QPoint(0, 0), canvas)
872 QtGui.QWidget.paintEvent(self, paintEvent)
875 return iter(self._filing)
878 return len(self._filing)
880 def _select_at(self, index):
881 self._selectionIndex = index
883 def _update_selection(self, lastMousePos):
884 radius = _radius_at(self._cachedCenterPosition, lastMousePos)
885 if radius < self._filing.innerRadius():
886 self._selectionIndex = PieFiling.SELECTION_CENTER
887 elif radius <= self._filing.outerRadius():
888 self._select_at(self.indexAt(lastMousePos))
890 self._selectionIndex = PieFiling.SELECTION_NONE
892 def _activate_at(self, index):
893 if index == PieFiling.SELECTION_NONE:
895 self.aboutToHide.emit()
898 elif index == PieFiling.SELECTION_CENTER:
899 child = self._filing.center()
901 child = self.itemAt(index)
903 if child.isEnabled():
904 child.action().trigger()
905 self.activated.emit(index)
908 self.aboutToHide.emit()
913 PieFiling.NULL_CENTER.setEnabled(False)
920 def _on_about_to_hide(app):
924 if __name__ == "__main__":
925 app = QtGui.QApplication([])
933 singleAction = QtGui.QAction(None)
934 singleAction.setText("Boo")
935 singleItem = QActionPieItem(singleAction)
937 spie.insertItem(singleItem)
941 oneAction = QtGui.QAction(None)
942 oneAction.setText("Chew")
943 oneItem = QActionPieItem(oneAction)
944 twoAction = QtGui.QAction(None)
945 twoAction.setText("Foo")
946 twoItem = QActionPieItem(twoAction)
947 iconTextAction = QtGui.QAction(None)
948 iconTextAction.setText("Icon")
949 iconTextAction.setIcon(QtGui.QIcon.fromTheme("gtk-close"))
950 iconTextItem = QActionPieItem(iconTextAction)
952 mpie.insertItem(oneItem)
953 mpie.insertItem(twoItem)
954 mpie.insertItem(oneItem)
955 mpie.insertItem(iconTextItem)
959 oneAction = QtGui.QAction(None)
960 oneAction.setText("Chew")
961 oneAction.triggered.connect(lambda: _print("Chew"))
962 oneItem = QActionPieItem(oneAction)
963 twoAction = QtGui.QAction(None)
964 twoAction.setText("Foo")
965 twoAction.triggered.connect(lambda: _print("Foo"))
966 twoItem = QActionPieItem(twoAction)
967 iconAction = QtGui.QAction(None)
968 iconAction.setIcon(QtGui.QIcon.fromTheme("gtk-open"))
969 iconAction.triggered.connect(lambda: _print("Icon"))
970 iconItem = QActionPieItem(iconAction)
971 iconTextAction = QtGui.QAction(None)
972 iconTextAction.setText("Icon")
973 iconTextAction.setIcon(QtGui.QIcon.fromTheme("gtk-close"))
974 iconTextAction.triggered.connect(lambda: _print("Icon and text"))
975 iconTextItem = QActionPieItem(iconTextAction)
977 mpie.set_center(iconItem)
978 mpie.insertItem(oneItem)
979 mpie.insertItem(twoItem)
980 mpie.insertItem(oneItem)
981 mpie.insertItem(iconTextItem)
983 mpie.aboutToHide.connect(lambda: _on_about_to_hide(app))
984 mpie.canceled.connect(lambda: _print("Canceled"))
987 oneAction = QtGui.QAction(None)
988 oneAction.setText("Chew")
989 oneAction.triggered.connect(lambda: _print("Chew"))
990 oneItem = QActionPieItem(oneAction)
991 twoAction = QtGui.QAction(None)
992 twoAction.setText("Foo")
993 twoAction.triggered.connect(lambda: _print("Foo"))
994 twoItem = QActionPieItem(twoAction)
995 iconAction = QtGui.QAction(None)
996 iconAction.setIcon(QtGui.QIcon.fromTheme("gtk-open"))
997 iconAction.triggered.connect(lambda: _print("Icon"))
998 iconItem = QActionPieItem(iconAction)
999 iconTextAction = QtGui.QAction(None)
1000 iconTextAction.setText("Icon")
1001 iconTextAction.setIcon(QtGui.QIcon.fromTheme("gtk-close"))
1002 iconTextAction.triggered.connect(lambda: _print("Icon and text"))
1003 iconTextItem = QActionPieItem(iconTextAction)
1004 pieFiling = PieFiling()
1005 pieFiling.set_center(iconItem)
1006 pieFiling.insertItem(oneItem)
1007 pieFiling.insertItem(twoItem)
1008 pieFiling.insertItem(oneItem)
1009 pieFiling.insertItem(iconTextItem)
1010 mpie = QPieDisplay(pieFiling)
1014 oneAction = QtGui.QAction(None)
1015 oneAction.setText("Chew")
1016 oneAction.triggered.connect(lambda: _print("Chew"))
1017 oneItem = QActionPieItem(oneAction)
1018 twoAction = QtGui.QAction(None)
1019 twoAction.setText("Foo")
1020 twoAction.triggered.connect(lambda: _print("Foo"))
1021 twoItem = QActionPieItem(twoAction)
1022 iconAction = QtGui.QAction(None)
1023 iconAction.setIcon(QtGui.QIcon.fromTheme("gtk-open"))
1024 iconAction.triggered.connect(lambda: _print("Icon"))
1025 iconItem = QActionPieItem(iconAction)
1026 iconTextAction = QtGui.QAction(None)
1027 iconTextAction.setText("Icon")
1028 iconTextAction.setIcon(QtGui.QIcon.fromTheme("gtk-close"))
1029 iconTextAction.triggered.connect(lambda: _print("Icon and text"))
1030 iconTextItem = QActionPieItem(iconTextAction)
1031 mpie = QPieButton(iconItem)
1032 mpie.set_center(iconItem)
1033 mpie.insertItem(oneItem)
1034 mpie.insertItem(twoItem)
1035 mpie.insertItem(oneItem)
1036 mpie.insertItem(iconTextItem)
1038 mpie.aboutToHide.connect(lambda: _on_about_to_hide(app))
1039 mpie.canceled.connect(lambda: _print("Canceled"))