Skip to content
Snippets Groups Projects
scheduler_event.py 11.5 KiB
Newer Older
  • Learn to ignore specific revisions
  • Andreas Bolin's avatar
    Andreas Bolin committed
    #!/usr/bin/env python3
    # -*- coding: utf-8 -*-
    """B-ASIC Scheduler-gui Graphics Graph Event Module.
    
    
    Contains the scheduler-gui SchedulerEvent class containing event filters and handlers for SchedulerItem objects.
    
    Andreas Bolin's avatar
    Andreas Bolin committed
    """
    
    
    Oscar Gustafsson's avatar
    Oscar Gustafsson committed
    from typing import List, Optional, overload
    
    Andreas Bolin's avatar
    Andreas Bolin committed
    
    # QGraphics and QPainter imports
    
    Oscar Gustafsson's avatar
    Oscar Gustafsson committed
    from qtpy.QtCore import QEvent, QObject, QPointF, Signal
    from qtpy.QtGui import QFocusEvent
    
    Andreas Bolin's avatar
    Andreas Bolin committed
    from qtpy.QtWidgets import (
    
    Oscar Gustafsson's avatar
    Oscar Gustafsson committed
        QGraphicsItem,
        QGraphicsSceneContextMenuEvent,
        QGraphicsSceneDragDropEvent,
        QGraphicsSceneHoverEvent,
        QGraphicsSceneMouseEvent,
        QGraphicsSceneWheelEvent,
    )
    
    Andreas Bolin's avatar
    Andreas Bolin committed
    
    
    Oscar Gustafsson's avatar
    Oscar Gustafsson committed
    from b_asic.schedule import Schedule
    
    from b_asic.scheduler_gui._preferences import OPERATION_GAP, OPERATION_HEIGHT
    
    from b_asic.scheduler_gui.axes_item import AxesItem
    from b_asic.scheduler_gui.operation_item import OperationItem
    from b_asic.scheduler_gui.timeline_item import TimelineItem
    
    class SchedulerEvent:  # PyQt5
        """Event filter and handlers for SchedulerItem"""
    
    Oscar Gustafsson's avatar
    Oscar Gustafsson committed
    
        class Signals(QObject):  # PyQt5
    
    Oscar Gustafsson's avatar
    Oscar Gustafsson committed
            """A class representing signals."""
    
    Oscar Gustafsson's avatar
    Oscar Gustafsson committed
    
    
    Andreas Bolin's avatar
    Andreas Bolin committed
            component_selected = Signal(str)
            schedule_time_changed = Signal()
    
    
    Oscar Gustafsson's avatar
    Oscar Gustafsson committed
        _axes: Optional[AxesItem]
    
    Oscar Gustafsson's avatar
    Oscar Gustafsson committed
        _current_pos: QPointF
        _delta_time: int
        _signals: Signals  # PyQt5
    
    Oscar Gustafsson's avatar
    Oscar Gustafsson committed
        _schedule: Schedule
    
        def __init__(self, parent: Optional[QGraphicsItem] = None):  # PyQt5
            super().__init__(parent=parent)
            self._signals = self.Signals()
    
    Andreas Bolin's avatar
    Andreas Bolin committed
    
    
        def is_component_valid_pos(self, item: OperationItem, pos: float) -> bool:
    
    Oscar Gustafsson's avatar
    Oscar Gustafsson committed
            raise NotImplementedError
    
    Oscar Gustafsson's avatar
    Oscar Gustafsson committed
    
        def is_valid_delta_time(self, delta_time: int) -> bool:
    
    Oscar Gustafsson's avatar
    Oscar Gustafsson committed
            raise NotImplementedError
    
    Andreas Bolin's avatar
    Andreas Bolin committed
    
    
    Oscar Gustafsson's avatar
    Oscar Gustafsson committed
        def set_schedule_time(self, delta_time: int) -> None:
    
    Oscar Gustafsson's avatar
    Oscar Gustafsson committed
            raise NotImplementedError
    
    Andreas Bolin's avatar
    Andreas Bolin committed
    
    
    Oscar Gustafsson's avatar
    Oscar Gustafsson committed
        def set_item_active(self, item: OperationItem) -> None:
            raise NotImplementedError
    
    Andreas Bolin's avatar
    Andreas Bolin committed
    
    
    Oscar Gustafsson's avatar
    Oscar Gustafsson committed
        def set_item_inactive(self, item: OperationItem) -> None:
            raise NotImplementedError
    
    Andreas Bolin's avatar
    Andreas Bolin committed
    
        #################
        #### Filters ####
        #################
        @overload
    
    Oscar Gustafsson's avatar
    Oscar Gustafsson committed
        def installSceneEventFilters(self, filterItems: QGraphicsItem) -> None:
            ...
    
    
    Andreas Bolin's avatar
    Andreas Bolin committed
        @overload
    
    Oscar Gustafsson's avatar
    Oscar Gustafsson committed
        def installSceneEventFilters(
            self, filterItems: List[QGraphicsItem]
        ) -> None:
            ...
    
    
    Andreas Bolin's avatar
    Andreas Bolin committed
        def installSceneEventFilters(self, filterItems) -> None:
    
    Oscar Gustafsson's avatar
    Oscar Gustafsson committed
            """
            Installs an event filter for *filterItems* on 'self', causing all events
            for *filterItems* to first pass through 'self's ``sceneEventFilter()``
            method. *filterItems* can be one object or a list of objects.
            """
    
            item: OperationItem
    
    Andreas Bolin's avatar
    Andreas Bolin committed
            for item in filterItems:
                item.installSceneEventFilter(self)
    
        @overload
    
    Oscar Gustafsson's avatar
    Oscar Gustafsson committed
        def removeSceneEventFilters(self, filterItems: QGraphicsItem) -> None:
            ...
    
    
    Andreas Bolin's avatar
    Andreas Bolin committed
        @overload
    
    Oscar Gustafsson's avatar
    Oscar Gustafsson committed
        def removeSceneEventFilters(
            self, filterItems: List[QGraphicsItem]
        ) -> None:
            ...
    
    
    Andreas Bolin's avatar
    Andreas Bolin committed
        def removeSceneEventFilters(self, filterItems) -> None:
            """Removes an event filter on 'filterItems' from 'self'. 'filterItems' can
            be one object or a list of objects."""
    
            item: OperationItem
    
    Andreas Bolin's avatar
    Andreas Bolin committed
            for item in filterItems:
                item.removeSceneEventFilter(self)
    
        def sceneEventFilter(self, item: QGraphicsItem, event: QEvent) -> bool:
    
    Oscar Gustafsson's avatar
    Oscar Gustafsson 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
    
    
            if isinstance(item, OperationItem):  # one component
    
    Andreas Bolin's avatar
    Andreas Bolin committed
                switch = {
                    # 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,
    
    Oscar Gustafsson's avatar
    Oscar Gustafsson committed
                    QEvent.GraphicsSceneMouseMove: self.comp_mouseMoveEvent,
                    QEvent.GraphicsSceneMousePress: self.comp_mousePressEvent,
                    QEvent.GraphicsSceneMouseRelease: self.comp_mouseReleaseEvent,
    
    Andreas Bolin's avatar
    Andreas Bolin committed
                    # QEvent.GraphicsSceneMouseDoubleClick:   self.comp_mouseDoubleClickEvent,
                    # QEvent.GraphicsSceneWheel:              self.comp_wheelEvent
                }
                handler = switch.get(event.type())
    
    
            elif isinstance(item, TimelineItem):  # the timeline
    
    Andreas Bolin's avatar
    Andreas Bolin committed
                switch = {
                    # QEvent.GraphicsSceneHoverEnter:         self.timeline_hoverEnterEvent,
                    # QEvent.GraphicsSceneHoverLeave:         self.timeline_hoverLeaveEvent,
    
    Oscar Gustafsson's avatar
    Oscar Gustafsson committed
                    QEvent.GraphicsSceneMouseMove: self.timeline_mouseMoveEvent,
                    QEvent.GraphicsSceneMousePress: self.timeline_mousePressEvent,
                    QEvent.GraphicsSceneMouseRelease: self.timeline_mouseReleaseEvent,
    
    Andreas Bolin's avatar
    Andreas Bolin committed
                }
                handler = switch.get(event.type())
    
            else:
    
    Oscar Gustafsson's avatar
    Oscar Gustafsson committed
                raise TypeError(
                    f"Received an unexpected event '{event.type()}' "
                    f"from an '{type(item).__name__}' object."
                )
    
    Andreas Bolin's avatar
    Andreas Bolin committed
    
            if handler is not None:
                handler(event)
                return True
    
    Oscar Gustafsson's avatar
    Oscar Gustafsson committed
            return False  # returns False if event is ignored and pass through event to its child
    
    Andreas Bolin's avatar
    Andreas Bolin committed
    
        # def sceneEvent(self, event: QEvent) -> bool:
        #     print(f'sceneEvent() --> {event.type()}')
        #     # event.accept()
        #     # QApplication.sendEvent(self.scene(), event)
        #     return False
    
        ###############################################
    
        #### Event Handlers: OperationItem ####
    
    Andreas Bolin's avatar
    Andreas Bolin committed
        ###############################################
    
    Oscar Gustafsson's avatar
    Oscar Gustafsson 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:
            ...
    
        def comp_hoverEnterEvent(self, event: QGraphicsSceneHoverEvent) -> None:
            ...
    
        def comp_hoverMoveEvent(self, event: QGraphicsSceneHoverEvent) -> None:
            ...
    
        def comp_hoverLeaveEvent(self, event: QGraphicsSceneHoverEvent) -> None:
            ...
    
    Andreas Bolin's avatar
    Andreas Bolin committed
    
        def comp_mouseMoveEvent(self, event: QGraphicsSceneMouseEvent) -> None:
    
    Oscar Gustafsson's avatar
    Oscar Gustafsson 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
    
    Oscar Gustafsson's avatar
    Oscar Gustafsson committed
            horizontally in x-axis scale steps.
            """
    
            def update_pos(item, dx, dy):
                posx = item.x() + dx
                posy = item.y() + dy * (OPERATION_GAP + OPERATION_HEIGHT)
                if self.is_component_valid_pos(item, posx):
                    item.setX(posx)
                    item.setY(posy)
    
                    self._current_pos.setX(self._current_pos.x() + dx)
    
                    self._current_pos.setY(self._current_pos.y() + dy)
    
    Andreas Bolin's avatar
    Andreas Bolin committed
                    self._redraw_lines(item)
    
                    self._schedule._y_locations[item.operation.graph_id] += dy
    
    Andreas Bolin's avatar
    Andreas Bolin committed
    
    
            item: OperationItem = self.scene().mouseGrabberItem()
    
            delta_x = (item.mapToParent(event.pos()) - self._current_pos).x()
    
            delta_y = (item.mapToParent(event.pos()) - self._current_pos).y()
    
            delta_y_steps = round(delta_y / (OPERATION_GAP + OPERATION_HEIGHT))
    
            if delta_x > 0.505:
    
                update_pos(item, 1, delta_y_steps)
    
            elif delta_x < -0.505:
    
                update_pos(item, -1, delta_y_steps)
            elif delta_y_steps != 0:
                update_pos(item, 0, delta_y_steps)
    
    Andreas Bolin's avatar
    Andreas Bolin committed
        def comp_mousePressEvent(self, event: QGraphicsSceneMouseEvent) -> None:
    
    Oscar Gustafsson's avatar
    Oscar Gustafsson committed
            """
            Changes the cursor to ClosedHandCursor when grabbing an object and
    
    Andreas Bolin's avatar
    Andreas Bolin committed
            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.
            """
    
            item: OperationItem = self.scene().mouseGrabberItem()
    
            self._signals.component_selected.emit(item.graph_id)
    
    Andreas Bolin's avatar
    Andreas Bolin committed
            self._current_pos = item.mapToParent(event.pos())
    
            self.set_item_active(item)
    
    Andreas Bolin's avatar
    Andreas Bolin committed
            event.accept()
    
        def comp_mouseReleaseEvent(self, event: QGraphicsSceneMouseEvent) -> None:
    
    Oscar Gustafsson's avatar
    Oscar Gustafsson committed
            """Change the cursor to OpenHandCursor when releasing an object."""
    
            item: OperationItem = self.scene().mouseGrabberItem()
    
            self.set_item_inactive(item)
    
    Andreas Bolin's avatar
    Andreas Bolin committed
            self.set_new_starttime(item)
    
    Oscar Gustafsson's avatar
    Oscar Gustafsson committed
            redraw = False
    
            if posx < 0:
                posx += self._schedule.schedule_time
    
    Oscar Gustafsson's avatar
    Oscar Gustafsson committed
                redraw = True
    
            if posx > self._schedule.schedule_time:
                posx = posx % self._schedule.schedule_time
    
    Oscar Gustafsson's avatar
    Oscar Gustafsson committed
                redraw = True
            if redraw:
    
                self._redraw_lines(item)
    
    Andreas Bolin's avatar
    Andreas Bolin committed
    
    
    Oscar Gustafsson's avatar
    Oscar Gustafsson committed
        def comp_mouseDoubleClickEvent(
            self, event: QGraphicsSceneMouseEvent
        ) -> None:
            ...
    
    Oscar Gustafsson's avatar
    Oscar Gustafsson committed
        def comp_wheelEvent(self, event: QGraphicsSceneWheelEvent) -> None:
            ...
    
    Andreas Bolin's avatar
    Andreas Bolin committed
    
        ###############################################
        #### Event Handlers: GraphicsLineTem       ####
        ###############################################
        def timeline_mouseMoveEvent(self, event: QGraphicsSceneMouseEvent) -> None:
    
    Oscar Gustafsson's avatar
    Oscar Gustafsson 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
    
    Oscar Gustafsson's avatar
    Oscar Gustafsson committed
            horizontally in x-axis scale steps.
            """
    
            def update_pos(item, dx):
                pos = item.x() + dx
                if self.is_valid_delta_time(self._delta_time + dx):
    
    Andreas Bolin's avatar
    Andreas Bolin committed
                    item.setX(pos)
    
                    self._current_pos.setX(self._current_pos.x() + dx)
                    self._delta_time += dx
    
    Andreas Bolin's avatar
    Andreas Bolin committed
                    item.set_text(self._delta_time)
    
    
            item: TimelineItem = self.scene().mouseGrabberItem()
    
    Andreas Bolin's avatar
    Andreas Bolin committed
            delta_x = (item.mapToParent(event.pos()) - self._current_pos).x()
            if delta_x > 0.505:
                update_pos(item, 1)
            elif delta_x < -0.505:
                update_pos(item, -1)
    
    
    Oscar Gustafsson's avatar
    Oscar Gustafsson committed
        def timeline_mousePressEvent(
            self, event: QGraphicsSceneMouseEvent
        ) -> None:
    
    Oscar Gustafsson's avatar
    Oscar Gustafsson committed
            """Store the current position in item's parent coordinates. 'event' will
    
    Andreas Bolin's avatar
    Andreas Bolin committed
            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.
            """
    
            item: TimelineItem = self.scene().mouseGrabberItem()
    
    Andreas Bolin's avatar
    Andreas Bolin committed
            self._delta_time = 0
            item.set_text(self._delta_time)
            item.show_label()
            self._current_pos = item.mapToParent(event.pos())
            event.accept()
    
    
    Oscar Gustafsson's avatar
    Oscar Gustafsson committed
        def timeline_mouseReleaseEvent(
            self, event: QGraphicsSceneMouseEvent
        ) -> None:
    
    Andreas Bolin's avatar
    Andreas Bolin committed
            """Updates the schedule time."""
    
            item: TimelineItem = self.scene().mouseGrabberItem()
    
    Andreas Bolin's avatar
    Andreas Bolin committed
            item.hide_label()
            if self._delta_time != 0:
                self.set_schedule_time(self._delta_time)
                self._signals.schedule_time_changed.emit()