Skip to content
Snippets Groups Projects
graphics_graph_event.py 11.6 KiB
Newer Older
  • Learn to ignore specific revisions
  • Andreas Bolin's avatar
    Andreas Bolin committed
    #!/usr/bin/env python3
    # -*- coding: utf-8 -*-
    
    Andreas Bolin's avatar
    Andreas Bolin committed
    """B-ASIC Scheduler-gui Graphics Graph Event Module.
    
    
    Andreas Bolin's avatar
    Andreas Bolin committed
    Contains the scheduler-gui GraphicsGraphEvent class containing event filters and handlers for GraphicsGraphItem objects.
    """
    
    Andreas Bolin's avatar
    Andreas Bolin committed
    
    import os
    import sys
    from typing     import Any, Optional
    from pprint     import pprint
    
    Andreas Bolin's avatar
    Andreas Bolin committed
    from typing     import Any, Union, Optional, overload, Final, final, List
    
    Andreas Bolin's avatar
    Andreas Bolin committed
    # from typing_extensions import Self, Final, Literal, LiteralString, TypeAlias, final
    import numpy    as np
    from copy       import deepcopy
    from itertools  import combinations
    
    import qtpy
    from qtpy import QtCore
    from qtpy import QtGui
    from qtpy import QtWidgets
    
    # QGraphics and QPainter imports
    from qtpy.QtCore    import (
    
    Andreas Bolin's avatar
    Andreas Bolin committed
        Qt, QObject, QRect, QRectF, QPoint, QSize, QSizeF, QByteArray, Slot, QEvent)
    
    Andreas Bolin's avatar
    Andreas Bolin committed
    from qtpy.QtGui     import (
        QPaintEvent, QPainter, QPainterPath, QColor, QBrush, QPen, QFont, QPolygon, QIcon, QPixmap,
    
    Andreas Bolin's avatar
    Andreas Bolin committed
        QLinearGradient, QTransform, QCursor, QFocusEvent)
    
    Andreas Bolin's avatar
    Andreas Bolin committed
    from qtpy.QtWidgets import (
        QGraphicsView, QGraphicsScene, QGraphicsWidget,
        QGraphicsLayout, QGraphicsLinearLayout, QGraphicsGridLayout, QGraphicsLayoutItem, QGraphicsAnchorLayout,
        QGraphicsItem, QGraphicsItemGroup, QGraphicsPathItem, QGraphicsLineItem, QGraphicsRectItem,
        QStyleOptionGraphicsItem, QWidget,
    
    Andreas Bolin's avatar
    Andreas Bolin committed
        QGraphicsObject, QGraphicsSceneMouseEvent, QGraphicsSceneHoverEvent, QGraphicsSceneContextMenuEvent,
        QGraphicsSceneDragDropEvent, QGraphicsSceneWheelEvent)
    
    Andreas Bolin's avatar
    Andreas Bolin committed
    from qtpy.QtCore    import (
        QPoint, QPointF)
    
    
    Andreas Bolin's avatar
    Andreas Bolin committed
    from abc import ABC
    
    Andreas Bolin's avatar
    Andreas Bolin committed
    from graphics_component_item    import GraphicsComponentItem
    from graphics_axes_item         import GraphicsAxesItem
    from graphics_timeline_item     import GraphicsTimelineItem
    
    Andreas Bolin's avatar
    Andreas Bolin committed
    
    
    Andreas Bolin's avatar
    Andreas Bolin committed
    
    # class GraphicsGraphEvent(ABC):
    
    Andreas Bolin's avatar
    Andreas Bolin committed
    class GraphicsGraphEvent(QGraphicsItem):
    
    Andreas Bolin's avatar
    Andreas Bolin committed
        """Event filter and handlers for GraphicsGraphItem"""
    
    Andreas Bolin's avatar
    Andreas Bolin committed
        _axes:          GraphicsAxesItem
    
    Andreas Bolin's avatar
    Andreas Bolin committed
        _current_pos:   QPointF
    
    Andreas Bolin's avatar
    Andreas Bolin committed
        _delta_time:    int
    
    Andreas Bolin's avatar
    Andreas Bolin committed
    
    
    Andreas Bolin's avatar
    Andreas Bolin committed
    
    
    Andreas Bolin's avatar
    Andreas Bolin committed
        #################
        #### Filters ####
        #################
    
    Andreas Bolin's avatar
    Andreas Bolin committed
        @overload
        def installSceneEventFilters(self, filterItems: QGraphicsItem) -> None: ...
        @overload
        def installSceneEventFilters(self, filterItems: List[QGraphicsItem]) -> None: ...
        def installSceneEventFilters(self, filterItems) -> None:
            """Installs an event filter for 'filterItems' on 'self', causing all events
            for 'filterItems' to first pass through 'self's sceneEventFilter()
            function. 'filterItems' can be one object or a list of objects."""
    
    Andreas Bolin's avatar
    Andreas Bolin committed
            item: GraphicsComponentItem
    
    Andreas Bolin's avatar
    Andreas Bolin committed
            for item in filterItems:
    
    Andreas Bolin's avatar
    Andreas Bolin committed
                item.installSceneEventFilter(self)
    
    Andreas Bolin's avatar
    Andreas Bolin committed
        
        @overload
        def removeSceneEventFilters(self, filterItems: QGraphicsItem) -> None: ...
        @overload
        def removeSceneEventFilters(self, filterItems: List[QGraphicsItem]) -> None: ...
        def removeSceneEventFilters(self, filterItems) -> None:
            """Removes an event filter on 'filterItems' from 'self'. 'filterItems' can
            be one object or a list of objects."""
    
    Andreas Bolin's avatar
    Andreas Bolin committed
            item: GraphicsComponentItem
    
    Andreas Bolin's avatar
    Andreas Bolin committed
            for item in filterItems:
    
    Andreas Bolin's avatar
    Andreas Bolin committed
                item.removeSceneEventFilter(self)
            
    
    Andreas Bolin's avatar
    Andreas Bolin committed
        
    
    Andreas Bolin's avatar
    Andreas Bolin committed
        def sceneEventFilter(self, item: QGraphicsItem, event: QEvent) -> bool:
    
    Andreas Bolin's avatar
    Andreas Bolin committed
            """Returns true if the event was filtered (i.e. stopped), otherwise false.
            If false is returned, the event is forwarded to the appropriate child in
            the event chain."""
    
    Andreas Bolin's avatar
    Andreas Bolin committed
            handler = None
    
    Andreas Bolin's avatar
    Andreas Bolin committed
    
    
    Andreas Bolin's avatar
    Andreas Bolin committed
            if isinstance(item, GraphicsComponentItem):     # one component
    
    Andreas Bolin's avatar
    Andreas Bolin committed
                switch = {
    
    Andreas Bolin's avatar
    Andreas Bolin committed
                    QEvent.FocusIn:                         self.comp_focusInEvent,
                    QEvent.GraphicsSceneContextMenu:        self.comp_contextMenuEvent,
                    QEvent.GraphicsSceneDragEnter:          self.comp_dragEnterEvent,
                    QEvent.GraphicsSceneDragMove:           self.comp_dragMoveEvent,
                    QEvent.GraphicsSceneDragLeave:          self.comp_dragLeaveEvent,
                    QEvent.GraphicsSceneDrop:               self.comp_dropEvent,
                    QEvent.GraphicsSceneHoverEnter:         self.comp_hoverEnterEvent,
                    QEvent.GraphicsSceneHoverMove:          self.comp_hoverMoveEvent,
                    QEvent.GraphicsSceneHoverLeave:         self.comp_hoverLeaveEvent,
                    QEvent.GraphicsSceneMouseMove:          self.comp_mouseMoveEvent,
                    QEvent.GraphicsSceneMousePress:         self.comp_mousePressEvent,
                    QEvent.GraphicsSceneMouseRelease:       self.comp_mouseReleaseEvent,
                    QEvent.GraphicsSceneMouseDoubleClick:   self.comp_mouseDoubleClickEvent,
                    QEvent.GraphicsSceneWheel:              self.comp_wheelEvent
    
    Andreas Bolin's avatar
    Andreas Bolin committed
                }
    
    Andreas Bolin's avatar
    Andreas Bolin committed
                handler = switch.get(event.type())
    
    Andreas Bolin's avatar
    Andreas Bolin committed
                
            elif isinstance(item, GraphicsTimelineItem):       # the timeline
                print(f'{type(item).__name__} received {event.type()}')
                switch = {
                    QEvent.GraphicsSceneHoverEnter:         self.timeline_hoverEnterEvent,
                    QEvent.GraphicsSceneHoverLeave:         self.timeline_hoverLeaveEvent,
                    QEvent.GraphicsSceneMouseMove:          self.timeline_mouseMoveEvent,
                    QEvent.GraphicsSceneMousePress:         self.timeline_mousePressEvent,
                    QEvent.GraphicsSceneMouseRelease:       self.timeline_mouseReleaseEvent,
                }
                handler = switch.get(event.type())
                
    
    Andreas Bolin's avatar
    Andreas Bolin committed
            else:
    
    Andreas Bolin's avatar
    Andreas Bolin committed
                raise TypeError(f"Received an unexpected event '{event.type()}' "
                                f"from an '{type(item).__name__}' object.")
    
    Andreas Bolin's avatar
    Andreas Bolin committed
            
    
    Andreas Bolin's avatar
    Andreas Bolin committed
            if handler is not None:
    
    Andreas Bolin's avatar
    Andreas Bolin committed
                handler(event)
                return True
    
    Andreas Bolin's avatar
    Andreas Bolin committed
            return False    # returns False if event is ignored and pass through event to its child
    
        # def sceneEvent(self, event: QEvent) -> bool:
        #     print(f'sceneEvent() --> {event.type()}')
        #     # event.accept()
        #     # QApplication.sendEvent(self.scene(), event)
        #     return False
    
    Andreas Bolin's avatar
    Andreas Bolin committed
        ###############################################
        #### Event Handlers: GraphicsComponentItem ####
        ###############################################
    
    Andreas Bolin's avatar
    Andreas Bolin committed
        def comp_focusInEvent(self, event: QFocusEvent) -> None: ...
        def comp_contextMenuEvent(self, event: QGraphicsSceneContextMenuEvent) -> None: ...
        def comp_dragEnterEvent(self, event: QGraphicsSceneDragDropEvent) -> None: ...
        def comp_dragMoveEvent(self, event: QGraphicsSceneDragDropEvent) -> None: ...
        def comp_dragLeaveEvent(self, event: QGraphicsSceneDragDropEvent) -> None: ...
        def comp_dropEvent(self, event: QGraphicsSceneDragDropEvent) -> None: ...
    
    Andreas Bolin's avatar
    Andreas Bolin committed
        
    
    Andreas Bolin's avatar
    Andreas Bolin committed
        def comp_hoverEnterEvent(self, event: QGraphicsSceneHoverEvent) -> None:
    
    Andreas Bolin's avatar
    Andreas Bolin committed
            """Changes the cursor to OpenHandCursor when hovering an object."""
            self.setCursor(QCursor(Qt.OpenHandCursor))
        
    
    Andreas Bolin's avatar
    Andreas Bolin committed
        def comp_hoverMoveEvent(self, event: QGraphicsSceneHoverEvent) -> None: ...
        
        def comp_hoverLeaveEvent(self, event: QGraphicsSceneHoverEvent) -> None:
    
    Andreas Bolin's avatar
    Andreas Bolin committed
            """Unsets the cursor to default cursor."""
            self.unsetCursor()
    
    Andreas Bolin's avatar
    Andreas Bolin committed
    
    
    Andreas Bolin's avatar
    Andreas Bolin committed
        def comp_mouseMoveEvent(self, event: QGraphicsSceneMouseEvent) -> None:
    
    Andreas Bolin's avatar
    Andreas Bolin committed
            """Set the position of the graphical element in the graphic scene, 
    
    Andreas Bolin's avatar
    Andreas Bolin committed
            translate coordinates of the cursor within the graphic element in the
            coordinate system of the parent object. The object can only move
            horizontally in x-axis scale steps."""
    
    Andreas Bolin's avatar
    Andreas Bolin committed
            # Qt.DragMoveCursor
            # button = event.button()
            item = self.scene().mouseGrabberItem()
            dx = (item.mapToParent(event.pos()) - self._current_pos).x()
    
    Andreas Bolin's avatar
    Andreas Bolin committed
            if dx > 0.505:
                pos = item.x() + 1.0
    
    Andreas Bolin's avatar
    Andreas Bolin committed
                if self.is_component_valid_pos(pos):
                    # self.prepareGeometryChange()
    
    Andreas Bolin's avatar
    Andreas Bolin committed
                    item.setX(pos)
    
    Andreas Bolin's avatar
    Andreas Bolin committed
                    self._current_pos.setX(self._current_pos.x() + 1.0)
            elif dx < -0.505:
                pos = item.x() - 1.0
    
    Andreas Bolin's avatar
    Andreas Bolin committed
                if self.is_component_valid_pos(pos):
                    # self.prepareGeometryChange()
    
    Andreas Bolin's avatar
    Andreas Bolin committed
                    item.setX(pos)
    
    Andreas Bolin's avatar
    Andreas Bolin committed
                    self._current_pos.setX(self._current_pos.x() - 1.0)
    
    Andreas Bolin's avatar
    Andreas Bolin committed
        
    
    Andreas Bolin's avatar
    Andreas Bolin committed
        def comp_mousePressEvent(self, event: QGraphicsSceneMouseEvent) -> None:
    
    Andreas Bolin's avatar
    Andreas Bolin committed
            """Changes the cursor to ClosedHandCursor when grabbing an object and
            stores the current position in item's parent coordinates. 'event' will
            by default be accepted, and this item is then the mouse grabber. This
            allows the item to receive future move, release and double-click events."""
    
    Andreas Bolin's avatar
    Andreas Bolin committed
            item: GraphicsComponentItem = self.scene().mouseGrabberItem()
            # op = self.schedule.sfg.find_by_id(item.op_id)
            # emit fill_info_table_component(op)
    
    Andreas Bolin's avatar
    Andreas Bolin committed
            self._current_pos = item.mapToParent(event.pos())
    
    Andreas Bolin's avatar
    Andreas Bolin committed
            self.setCursor(QCursor(Qt.ClosedHandCursor))
    
    Andreas Bolin's avatar
    Andreas Bolin committed
            event.accept()
    
    Andreas Bolin's avatar
    Andreas Bolin committed
        
    
    Andreas Bolin's avatar
    Andreas Bolin committed
        def comp_mouseReleaseEvent(self, event: QGraphicsSceneMouseEvent) -> None:
    
    Andreas Bolin's avatar
    Andreas Bolin committed
            """Changes the cursor to OpenHandCursor when releasing an object."""
    
    Andreas Bolin's avatar
    Andreas Bolin committed
            self.setCursor(QCursor(Qt.OpenHandCursor))
    
    Andreas Bolin's avatar
    Andreas Bolin committed
    
    
    Andreas Bolin's avatar
    Andreas Bolin committed
        def comp_mouseDoubleClickEvent(self, event: QGraphicsSceneMouseEvent) -> None: ...
        def comp_wheelEvent(self, event: QGraphicsSceneWheelEvent) -> None: ...
    
    Andreas Bolin's avatar
    Andreas Bolin committed
        
        ###############################################
    
    Andreas Bolin's avatar
    Andreas Bolin committed
        #### Event Handlers: GraphicsLineTem       ####
    
    Andreas Bolin's avatar
    Andreas Bolin committed
        ###############################################
    
    Andreas Bolin's avatar
    Andreas Bolin committed
        def timeline_hoverEnterEvent(self, event: QGraphicsSceneHoverEvent) -> None:
            """Changes the cursor to SizeHorCursor when hovering an object."""
            self.setCursor(QCursor(Qt.SizeHorCursor))
        
        def timeline_hoverLeaveEvent(self, event: QGraphicsSceneHoverEvent) -> None:
            """Unsets the cursor to default cursor."""
            self.unsetCursor()
    
        def timeline_mouseMoveEvent(self, event: QGraphicsSceneMouseEvent) -> None:
            """Set the position of the graphical element in the graphic scene, 
            translate coordinates of the cursor within the graphic element in the
            coordinate system of the parent object. The object can only move
            horizontally in x-axis scale steps."""
            # Qt.DragMoveCursor
            # button = event.button()
    
    Andreas Bolin's avatar
    Andreas Bolin committed
            item: GraphicsTimelineItem = self.scene().mouseGrabberItem()
    
    Andreas Bolin's avatar
    Andreas Bolin committed
            dx = (item.mapToParent(event.pos()) - self._current_pos).x()
            if dx > 0.505:
                pos = item.x() + 1.0
                if self.is_valid_delta_time(self._delta_time + 1):
                    # self.prepareGeometryChange()
                    item.setX(pos)
                    self._current_pos.setX(self._current_pos.x() + 1.0)
                    self._delta_time += 1
    
    Andreas Bolin's avatar
    Andreas Bolin committed
                    item.set_text(self._delta_time)
    
    Andreas Bolin's avatar
    Andreas Bolin committed
            elif dx < -0.505:
                pos = item.x() - 1.0
                if self.is_valid_delta_time(self._delta_time - 1):
                    # self.prepareGeometryChange()
                    item.setX(pos)
                    self._current_pos.setX(self._current_pos.x() - 1.0)
                    self._delta_time -= 1
    
    Andreas Bolin's avatar
    Andreas Bolin committed
                    item.set_text(self._delta_time)
    
    Andreas Bolin's avatar
    Andreas Bolin committed
                    
        
        def timeline_mousePressEvent(self, event: QGraphicsSceneMouseEvent) -> None:
            """Stores the current position in item's parent coordinates. 'event' will
            by default be accepted, and this item is then the mouse grabber. This
            allows the item to receive future move, release and double-click events."""
    
    Andreas Bolin's avatar
    Andreas Bolin committed
            item: GraphicsTimelineItem = self.scene().mouseGrabberItem()
    
    Andreas Bolin's avatar
    Andreas Bolin committed
            self._delta_time = 0
    
    Andreas Bolin's avatar
    Andreas Bolin committed
            item.set_text(self._delta_time)
            item.show_label()
            self._current_pos = item.mapToParent(event.pos())
    
    Andreas Bolin's avatar
    Andreas Bolin committed
            event.accept()
        
        def timeline_mouseReleaseEvent(self, event: QGraphicsSceneMouseEvent) -> None:
            """Updates the schedule time."""
    
    Andreas Bolin's avatar
    Andreas Bolin committed
            item: GraphicsTimelineItem = self.scene().mouseGrabberItem()
            item.hide_label()
    
    Andreas Bolin's avatar
    Andreas Bolin committed
            if self._delta_time != 0:
                self.set_schedule_time(self._delta_time)