class InteractiveItem(QGraphicsRectItem): def __init__(self, *arg, **karg): QGraphicsRectItem.__init__(self, *arg, **karg) self.node = None self.label = None self.setCursor(QtCore.Qt.PointingHandCursor) self.setAcceptsHoverEvents(True) def hoverEnterEvent (self, e): # There are many ways of adding interactive elements. With the # following code, I show/hide a text item over my custom # DynamicItemFace if not self.label: self.label = QGraphicsRectItem() self.label.setParentItem(self) # This is to ensure that the label is rendered over the # rest of item children (default ZValue for items is 0) self.label.setZValue(1) self.label.setBrush(QBrush(QColor("white"))) self.label.text = QGraphicsSimpleTextItem() self.label.text.setParentItem(self.label) self.label.text.setText(self.node.name) self.label.setRect(self.label.text.boundingRect()) self.label.setVisible(True) def hoverLeaveEvent(self, e): if self.label: self.label.setVisible(False)
class InteractiveItem(QGraphicsRectItem): def __init__(self, *arg, **karg): QGraphicsRectItem.__init__(self, *arg, **karg) self.node = None self.label = None self.setCursor(QtCore.Qt.PointingHandCursor) self.setAcceptsHoverEvents(True) def hoverEnterEvent(self, e): # There are many ways of adding interactive elements. With the # following code, I show/hide a text item over my custom # DynamicItemFace if not self.label: self.label = QGraphicsRectItem() self.label.setParentItem(self) # This is to ensure that the label is rendered over the # rest of item children (default ZValue for items is 0) self.label.setZValue(1) self.label.setBrush(QBrush(QColor("white"))) self.label.text = QGraphicsSimpleTextItem() self.label.text.setParentItem(self.label) self.label.text.setText(self.node.name) self.label.setRect(self.label.text.boundingRect()) self.label.setVisible(True) def hoverLeaveEvent(self, e): if self.label: self.label.setVisible(False)
class RectangleSelectionAction(UserInteraction): """ Select items in the scene using a Rectangle selection """ def __init__(self, document, *args, **kwargs): UserInteraction.__init__(self, document, *args, **kwargs) # The initial selection at drag start self.initial_selection = None # Selection when last updated in a mouseMoveEvent self.last_selection = None # A selection rect (`QRectF`) self.selection_rect = None # Keyboard modifiers self.modifiers = 0 def mousePressEvent(self, event): pos = event.scenePos() any_item = self.scene.item_at(pos) if not any_item and event.button() & Qt.LeftButton: self.modifiers = event.modifiers() self.selection_rect = QRectF(pos, QSizeF(0, 0)) self.rect_item = QGraphicsRectItem( self.selection_rect.normalized() ) self.rect_item.setPen( QPen(QBrush(QColor(51, 153, 255, 192)), 0.4, Qt.SolidLine, Qt.RoundCap) ) self.rect_item.setBrush( QBrush(QColor(168, 202, 236, 192)) ) self.rect_item.setZValue(-100) # Clear the focus if necessary. if not self.scene.stickyFocus(): self.scene.clearFocus() if not self.modifiers & Qt.ControlModifier: self.scene.clearSelection() event.accept() return True else: self.cancel(self.ErrorReason) return False def mouseMoveEvent(self, event): if not self.rect_item.scene(): # Add the rect item to the scene when the mouse moves. self.scene.addItem(self.rect_item) self.update_selection(event) return True def mouseReleaseEvent(self, event): if event.button() == Qt.LeftButton: if self.initial_selection is None: # A single click. self.scene.clearSelection() else: self.update_selection(event) self.end() return True def update_selection(self, event): """ Update the selection rectangle from a QGraphicsSceneMouseEvent `event` instance. """ if self.initial_selection is None: self.initial_selection = set(self.scene.selectedItems()) self.last_selection = self.initial_selection pos = event.scenePos() self.selection_rect = QRectF(self.selection_rect.topLeft(), pos) # Make sure the rect_item does not cause the scene rect to grow. rect = self._bound_selection_rect(self.selection_rect.normalized()) # Need that 0.5 constant otherwise the sceneRect will still # grow (anti-aliasing correction by QGraphicsScene?) pw = self.rect_item.pen().width() + 0.5 self.rect_item.setRect(rect.adjusted(pw, pw, -pw, -pw)) selected = self.scene.items(self.selection_rect.normalized(), Qt.IntersectsItemShape, Qt.AscendingOrder) selected = set([item for item in selected if \ item.flags() & Qt.ItemIsSelectable]) if self.modifiers & Qt.ControlModifier: for item in selected | self.last_selection | \ self.initial_selection: item.setSelected( (item in selected) ^ (item in self.initial_selection) ) else: for item in selected.union(self.last_selection): item.setSelected(item in selected) self.last_selection = set(self.scene.selectedItems()) def end(self): self.initial_selection = None self.last_selection = None self.modifiers = 0 self.rect_item.hide() if self.rect_item.scene() is not None: self.scene.removeItem(self.rect_item) UserInteraction.end(self) def viewport_rect(self): """ Return the bounding rect of the document's viewport on the scene. """ view = self.document.view() vsize = view.viewport().size() viewportrect = QRect(0, 0, vsize.width(), vsize.height()) return view.mapToScene(viewportrect).boundingRect() def _bound_selection_rect(self, rect): """ Bound the selection `rect` to a sensible size. """ srect = self.scene.sceneRect() vrect = self.viewport_rect() maxrect = srect.united(vrect) return rect.intersected(maxrect)
class RectangleSelectionAction(UserInteraction): """ Select items in the scene using a Rectangle selection """ def __init__(self, document, *args, **kwargs): UserInteraction.__init__(self, document, *args, **kwargs) # The initial selection at drag start self.initial_selection = None # Selection when last updated in a mouseMoveEvent self.last_selection = None # A selection rect (`QRectF`) self.selection_rect = None # Keyboard modifiers self.modifiers = 0 def mousePressEvent(self, event): pos = event.scenePos() any_item = self.scene.item_at(pos) if not any_item and event.button() & Qt.LeftButton: self.modifiers = event.modifiers() self.selection_rect = QRectF(pos, QSizeF(0, 0)) self.rect_item = QGraphicsRectItem( self.selection_rect.normalized()) self.rect_item.setPen( QPen(QBrush(QColor(51, 153, 255, 192)), 0.4, Qt.SolidLine, Qt.RoundCap)) self.rect_item.setBrush(QBrush(QColor(168, 202, 236, 192))) self.rect_item.setZValue(-100) # Clear the focus if necessary. if not self.scene.stickyFocus(): self.scene.clearFocus() if not self.modifiers & Qt.ControlModifier: self.scene.clearSelection() event.accept() return True else: self.cancel(self.ErrorReason) return False def mouseMoveEvent(self, event): if not self.rect_item.scene(): # Add the rect item to the scene when the mouse moves. self.scene.addItem(self.rect_item) self.update_selection(event) return True def mouseReleaseEvent(self, event): if event.button() == Qt.LeftButton: if self.initial_selection is None: # A single click. self.scene.clearSelection() else: self.update_selection(event) self.end() return True def update_selection(self, event): """ Update the selection rectangle from a QGraphicsSceneMouseEvent `event` instance. """ if self.initial_selection is None: self.initial_selection = set(self.scene.selectedItems()) self.last_selection = self.initial_selection pos = event.scenePos() self.selection_rect = QRectF(self.selection_rect.topLeft(), pos) # Make sure the rect_item does not cause the scene rect to grow. rect = self._bound_selection_rect(self.selection_rect.normalized()) # Need that 0.5 constant otherwise the sceneRect will still # grow (anti-aliasing correction by QGraphicsScene?) pw = self.rect_item.pen().width() + 0.5 self.rect_item.setRect(rect.adjusted(pw, pw, -pw, -pw)) selected = self.scene.items(self.selection_rect.normalized(), Qt.IntersectsItemShape, Qt.AscendingOrder) selected = set([item for item in selected if \ item.flags() & Qt.ItemIsSelectable]) if self.modifiers & Qt.ControlModifier: for item in selected | self.last_selection | \ self.initial_selection: item.setSelected((item in selected) ^ (item in self.initial_selection)) else: for item in selected.union(self.last_selection): item.setSelected(item in selected) self.last_selection = set(self.scene.selectedItems()) def end(self): self.initial_selection = None self.last_selection = None self.modifiers = 0 self.rect_item.hide() if self.rect_item.scene() is not None: self.scene.removeItem(self.rect_item) UserInteraction.end(self) def viewport_rect(self): """ Return the bounding rect of the document's viewport on the scene. """ view = self.document.view() vsize = view.viewport().size() viewportrect = QRect(0, 0, vsize.width(), vsize.height()) return view.mapToScene(viewportrect).boundingRect() def _bound_selection_rect(self, rect): """ Bound the selection `rect` to a sensible size. """ srect = self.scene.sceneRect() vrect = self.viewport_rect() maxrect = srect.united(vrect) return rect.intersected(maxrect)
class MJScene(QGraphicsScene): """our scene with a potential Qt bug fix""" def __init__(self): QGraphicsScene.__init__(self) self.__disableFocusRect = False self._focusBoard = None self.focusRect = QGraphicsRectItem() pen = QPen(QColor(Qt.blue)) pen.setWidth(6) self.focusRect.setPen(pen) self.addItem(self.focusRect) self.focusRect.setZValue(ZValues.marker) self.focusRect.hide() def __focusRectVisible(self): """should we show it?""" game = InternalParameters.field.game board = self._focusBoard return bool(not self.__disableFocusRect and board and board.hasFocus and board.focusTile and game and not game.autoPlay) @apply def disableFocusRect(): # pylint: disable=E0202 """suppress focusrect""" def fget(self): # pylint: disable=W0212 return self.__disableFocusRect def fset(self, value): # pylint: disable=W0212 # always place or hide, even if value does not change self.__disableFocusRect = value if value: self.focusRect.hide() else: self.placeFocusRect() return property(**locals()) @apply def focusBoard(): # pylint: disable=E0202 """get / set the board that has its focusRect shown""" def fget(self): # pylint: disable=W0212 return self._focusBoard def fset(self, board): # pylint: disable=W0212 self._focusBoard = board focusTile = board.focusTile if board else None if focusTile: focusTile.graphics.setFocus() self.placeFocusRect() self.focusRect.setVisible(self.__focusRectVisible()) return property(**locals()) def placeFocusRect(self): """show a blue rect around tile""" board = self._focusBoard if isAlive(board) and self.__focusRectVisible(): rect = board.tileFaceRect() rect.setWidth(rect.width()*board.focusRectWidth()) self.focusRect.setRect(rect) self.focusRect.setPos(board.focusTile.graphics.pos()) self.focusRect.setRotation(board.sceneRotation()) self.focusRect.setScale(board.scale()) self.focusRect.show() else: self.focusRect.hide() def graphicsTileItems(self): """returns all GraphicsTileItems in the scene""" return (x for x in self.items() if isinstance(x, GraphicsTileItem)) def nonTiles(self): """returns all other items in the scene""" return (x for x in self.items() if not isinstance(x, GraphicsTileItem)) def removeTiles(self): """remove all tiles from scene""" for item in self.graphicsTileItems(): self.removeItem(item) self.focusRect.hide()
class MJScene(QGraphicsScene): """our scene with a potential Qt bug fix""" def __init__(self): QGraphicsScene.__init__(self) self.__disableFocusRect = False self._focusBoard = None self.focusRect = QGraphicsRectItem() pen = QPen(QColor(Qt.blue)) pen.setWidth(6) self.focusRect.setPen(pen) self.addItem(self.focusRect) self.focusRect.setZValue(ZValues.marker) self.focusRect.hide() def focusInEvent(self, event): """work around a qt bug. See https://bugreports.qt-project.org/browse/QTBUG-32890 This can be reproduced as follows: ./kajongg.py --game=whatever --autoplay=SomeRuleset such that the human player is the first one to discard a tile. wait until the main screen has been built click with the mouse into the middle of that window press left arrow key this will violate the assertion in GraphicsTileItem.keyPressEvent """ prev = self.focusItem() QGraphicsScene.focusInEvent(self, event) if prev and bool(prev.flags() & QGraphicsItem.ItemIsFocusable) and prev != self.focusItem(): self.setFocusItem(prev) def __focusRectVisible(self): """should we show it?""" game = Internal.field.game board = self._focusBoard return bool(not self.__disableFocusRect and board and board.hasFocus and board.focusTile and game and not game.autoPlay) @property def disableFocusRect(self): """suppress focusrect""" return self.__disableFocusRect @disableFocusRect.setter def disableFocusRect(self, value): """always place or hide, even if value does not change""" self.__disableFocusRect = value if value: self.focusRect.hide() else: self.placeFocusRect() @property def focusBoard(self): """get / set the board that has its focusRect shown""" return self._focusBoard @focusBoard.setter def focusBoard(self, board): """get / set the board that has its focusRect shown""" self._focusBoard = board focusTile = board.focusTile if board else None if focusTile: focusTile.graphics.setFocus() self.placeFocusRect() self.focusRect.setVisible(self.__focusRectVisible()) def placeFocusRect(self): """show a blue rect around tile""" board = self._focusBoard if isAlive(board) and self.__focusRectVisible(): rect = board.tileFaceRect() rect.setWidth(rect.width()*board.focusRectWidth()) self.focusRect.setRect(rect) self.focusRect.setPos(board.focusTile.graphics.pos()) self.focusRect.setRotation(board.sceneRotation()) self.focusRect.setScale(board.scale()) self.focusRect.show() else: self.focusRect.hide() def graphicsTileItems(self): """returns all GraphicsTileItems in the scene""" return (x for x in self.items() if isinstance(x, GraphicsTileItem)) def nonTiles(self): """returns all other items in the scene""" return (x for x in self.items() if not isinstance(x, GraphicsTileItem)) def removeTiles(self): """remove all tiles from scene""" for item in self.graphicsTileItems(): self.removeItem(item) self.focusRect.hide()
def draw_colored_boxes(self, h): stick_colors = self.colors num_col_red = stick_colors.count("red") num_col_orange = stick_colors.count("orange") num_col_yellow = stick_colors.count("#EFDB00") num_col_gray = stick_colors.count("gray") colored_box_h = self.height + h - 5 if num_col_red: rect_red = QGraphicsRectItem(self.col_w * 0 - 1, self.coordY(self.ylim[1]) - 5, self.col_w * num_col_red, colored_box_h) qpen = QPen(QColor('red')) qpen.setWidth(1) qpen.setStyle( 5) # dash line : http://doc.qt.io/qt-4.8/qt.html#PenStyle-enum rect_red.setPen(qpen) rect_red.setBrush(QColor("#FFD1D1")) rect_red.setZValue(-1) rect_red.setParentItem(self.item) #rect_red.setOpacity(0.5) if num_col_orange: rect_orange = QGraphicsRectItem(self.col_w * num_col_red + 1, self.coordY(self.ylim[1]) - 5, self.col_w * num_col_orange - 2, colored_box_h) qpen = QPen(QColor('orange')) qpen.setWidth(1) qpen.setStyle( 5) # dash line : http://doc.qt.io/qt-4.8/qt.html#PenStyle-enum rect_orange.setPen(qpen) rect_orange.setBrush(QColor("#FFE3B0")) rect_orange.setZValue(-1) rect_orange.setParentItem(self.item) #rect_orange.setOpacity(0.5) if num_col_yellow: rect_yellow = QGraphicsRectItem( self.col_w * (num_col_orange + num_col_red) + 1, self.coordY(self.ylim[1]) - 5, self.col_w * num_col_yellow - 2, colored_box_h) qpen = QPen(QColor('#EFDB00')) qpen.setWidth(1) qpen.setStyle( 5) # dash line : http://doc.qt.io/qt-4.8/qt.html#PenStyle-enum rect_yellow.setPen(qpen) rect_yellow.setBrush(QColor("#FBFFA5")) rect_yellow.setZValue(-1) rect_yellow.setParentItem(self.item) #rect_yellow.setOpacity(0.5) if num_col_gray: rect_gray = QGraphicsRectItem( self.col_w * (num_col_orange + num_col_red + num_col_yellow) + 1, self.coordY(self.ylim[1]) - 5, self.col_w * num_col_gray, colored_box_h) qpen = QPen(QColor('gray')) qpen.setWidth(1) qpen.setStyle( 5) # dash line : http://doc.qt.io/qt-4.8/qt.html#PenStyle-enum rect_gray.setPen(qpen) rect_gray.setBrush(QColor("#E2E2E2")) rect_gray.setZValue(-1) rect_gray.setParentItem(self.item)