class BaseViewWidget(QtOpenGL.QGLWidget): def __init__(self, parent: Optional[QtWidgets.QWidget] = None) -> None: super(BaseViewWidget, self).__init__(parent) if hasattr(QtOpenGL.QGLFormat, 'setVersion'): f = QtOpenGL.QGLFormat() f.setVersion(3, 2) f.setProfile(QtOpenGL.QGLFormat.CoreProfile) c = QtOpenGL.QGLContext(f) QtOpenGL.QGLWidget.__init__(self, c, parent) else: QtOpenGL.QGLWidget.__init__(self, parent) self.__gl_initialized = False self.viewState = ViewPort(self.width(), self.height()) self.viewState.changed.connect(self.update) self.lastPoint = QtCore.QPoint(0, 0) # Nav Handling self.active_drag = None self.mouse_wheel_emu = None self.action_log_cb: 'Optional[Callable[[VarArg(Any)], None]]' = None self.interactionDelegate = None self.setMouseTracking(True) # TODO refine type self.selectionList: Set[Any] = set() self.open_time = time.time() # OpenGL shared resources object. Initialized during initializeGL self.gls = GLShared() # self.local_actions_map = { (EventID.Mouse_B2_DragStart, Modifier(0)): EVT_START_FIELD_DRAG, (EventID.Mouse_B2, Modifier(0)): EVT_STOP_FIELD_DRAG, } self.id_actions_map = {} self.notify_changed_actions = [] def internal_event(self, action_event: ToolActionEvent) -> None: if action_event.code == EVT_START_FIELD_DRAG: self.active_drag = MoveDragHandler(self.viewState, action_event.cursor_pos) elif action_event.code == EVT_STOP_FIELD_DRAG: self.active_drag = None def _log_action(self, *args: Any) -> None: if self.action_log_cb is not None: self.action_log_cb(*args) def dispatchActionEvent(self, point: QtCore.QPoint, event_id: int, modifiers: Modifier, amount: int = 1) -> bool: key = (event_id, modifiers) if key in self.local_actions_map: code = self.local_actions_map[key] cb = self.internal_event self._log_action(key, "local") elif key in self.id_actions_map: if self.interactionDelegate is None: self._log_action(key, "noid") return False self._log_action(key, "delegate", self.id_actions_map[key].description) code = self.id_actions_map[key].event_code cb = self.interactionDelegate.tool_event else: self._log_action(key, "unhandled") return False pt = QPoint_to_point(point) w_pt = self.viewState.tfV2W(pt) event = ToolActionEvent(code, pt, w_pt, amount) cb(event) return True def eventFilter(self, target: Any, qevent: QtCore.QEvent) -> bool: if self.interactionDelegate is None: return False if qevent.type() == QtCore.QEvent.KeyPress: q_key_event = cast(QtGui.QKeyEvent, qevent) event_id = EventID.from_key_event(q_key_event) modifiers = Modifier.from_qevent(qevent) ate_event = self.dispatchActionEvent(self.lastPoint, event_id, modifiers) if ate_event: self.update() return ate_event return False def setSelectionList(self, l: Optional[Sequence[Geom]]) -> None: old = self.selectionList if l is None: self.selectionList = set() else: self.selectionList = set(l) if old != self.selectionList: self.update() def setInteractionDelegate(self, ed: 'BaseToolController') -> None: self.selectionList = set() if self.interactionDelegate == ed: return if self.interactionDelegate: self.interactionDelegate.finalize() # Build a map of internal event IDs to actions on the delegate self.id_actions_map = {} for action in ed.tool_actions: # TODO: customized shortcuts for shortcut in action.default_shortcuts: self.id_actions_map[(shortcut.evtid, shortcut.modifiers)] = action for notify_changed in self.notify_changed_actions: notify_changed() self.interactionDelegate = ed ed.initialize() # If we have an overlay, GLinit it only if we've initialized shared state # If we haven't, self.initializeGL will be called anyways before render, which will end up calling # overlay.initializeGL if ed.overlay is not None and self.gls: self.makeCurrent() ed.overlay.initializeGL(self.gls) self.update() def mousePressEvent(self, event: QtGui.QMouseEvent) -> None: self.lastPoint = event.pos() event_id = EventID.from_mouse_event(event) modifiers = Modifier.from_qevent(event) self.dispatchActionEvent(event.pos(), event_id, modifiers) self.update() def mouseMoveEvent(self, qevent: QtGui.QMouseEvent) -> None: self.lastPoint = qevent.pos() if self.active_drag: self.active_drag.move(QPoint_to_point(qevent.pos())) elif self.active_drag is None and not self.mouse_wheel_emu: if self.interactionDelegate is not None: pos = QPoint_to_point(qevent.pos()) # TODO - revisit behaviour? potential_actions = [] for (event_id, modifiers), act in self.id_actions_map.items(): if event_id.mouse_triggered(): potential_actions.append(act) event = MoveEvent( pos, self.viewState.tfV2W(pos), potential_actions) self.interactionDelegate.mouseMoveEvent(event) self.update() def mouseReleaseEvent(self, event: QtGui.QMouseEvent) -> None: event_id = EventID.from_mouse_event(event) modifiers = Modifier.from_qevent(event) self.dispatchActionEvent(event.pos(), event_id, modifiers) self.update() def zoom(self, step: int, around_point: Point2) -> None: sf = 1.1 ** step self.viewState.zoom_at(around_point, sf) def wheelEvent(self, event: QtGui.QWheelEvent) -> None: cpt = QPoint_to_point(event.pos()) if not event.modifiers(): step = event.angleDelta().y() / 120.0 self.zoom(step, cpt) else: if self.interactionDelegate: step = event.angleDelta().y() / 120.0 if step < 0: step = - step code = EventID.Mouse_WheelDown else: code = EventID.Mouse_WheelUp w_pt = self.viewState.tfV2W(cpt) tool_event = ToolActionEvent(code, cpt, w_pt, step) self.interactionDelegate.tool_event(tool_event) self.update() def initializeGL(self) -> None: assert not self.__gl_initialized self.__gl_initialized = True GL.glClearColor(0, 0, 0, 1) self.gls.initializeGL() GL.glGetError() GL.glEnable(GL.GL_BLEND) # Additive Blending GL.glBlendFunc(GL.GL_SRC_ALPHA, GL.GL_ONE) self.reinit() if self.interactionDelegate is not None and self.interactionDelegate.overlay is not None: self.interactionDelegate.overlay.initializeGL(self.gls) pass def reinit(self) -> None: pass def paintGL(self) -> None: if not self.__gl_initialized: self.initializeGL() GL.glClear(GL.GL_COLOR_BUFFER_BIT, GL.GL_DEPTH_BUFFER_BIT) self.render() def render_tool(self) -> None: if self.interactionDelegate and self.interactionDelegate.overlay: self.interactionDelegate.overlay.render(self.viewState, self.compositor) def resizeGL(self, width: int, height: int) -> None: if width == 0 or height == 0: return size = max(width, height) GL.glViewport((width - size) // 2, (height - size) // 2, size, size) self.viewState.resize(width, height) def isModified(self) -> bool: return self.modified
class BaseViewWidget(QtOpenGL.QGLWidget): def __init__(self, parent=None): if hasattr(QtOpenGL.QGLFormat, 'setVersion'): f = QtOpenGL.QGLFormat(); f.setVersion(3, 2) f.setProfile(QtOpenGL.QGLFormat.CoreProfile) c = QtOpenGL.QGLContext(f) QtOpenGL.QGLWidget.__init__(self, c, parent) else: QtOpenGL.QGLWidget.__init__(self, parent) self.viewState = ViewState(self.width(), self.height()) self.viewState.changed.connect(self.update) # Nav Handling self.move_dragging = False self.move_dragged = False self.mwemu = False self.lastPoint = None self.interactionDelegate = None self.setMouseTracking(True) self.selectionList = set() self.open_time = time.time() # OpenGL shared resources object. Initialized during initializeGL self.gls = GLShared() def eventFilter(self, target, event): if self.interactionDelegate is None: return False if event.type() == QtCore.QEvent.KeyRelease: s = self.interactionDelegate.keyReleaseEvent(event) self.update() return s if event.type() == QtCore.QEvent.KeyPress: s = self.interactionDelegate.keyPressEvent(event) self.update() return s return False def setSelectionList(self, l): old = self.selectionList if l is None: self.selectionList = set() else: self.selectionList = set(l) if old != self.selectionList: self.update() def setInteractionDelegate(self, ed): """ :param ed: :type ed: pcbre.ui.tools.basetool.BaseToolController """ self.selectionList = set() if self.interactionDelegate == ed: return if self.interactionDelegate: self.interactionDelegate.changed.disconnect(self.update) self.interactionDelegate.finalize() self.interactionDelegate = ed ed.changed.connect(self.update) ed.initialize() # If we have an overlay, GLinit it only if we've initialized shared state # If we haven't, self.initializeGL will be called anyways before render, which will end up calling # overlay.initializeGL if ed.overlay is not None and self.gls: self.makeCurrent() ed.overlay.initializeGL(self.gls) self.update() def mousePressEvent(self, event): self.lastPoint = event.pos() self.move_dragged = False if event.button() == MOVE_MOUSE_BUTTON: self.move_dragging = True return elif event.button() == QtCore.Qt.MiddleButton: self.mwemu = True return elif not self.move_dragging and not self.mwemu: if self.interactionDelegate is not None: self.interactionDelegate.mousePressEvent(event) self.update() def mouseMoveEvent(self, event): if self.lastPoint is None: self.lastPoint = event.pos() return delta_px = event.pos() - self.lastPoint lastpoint_real = self.viewState.tfV2P(Point2(self.lastPoint)) newpoint_real = self.viewState.tfV2P(Point2(event.pos())) lx,ly = lastpoint_real nx,ny = newpoint_real delta = nx-lx, ny-ly self.lastPoint = event.pos() needs_update = False if (event.buttons() & MOVE_MOUSE_BUTTON) and self.move_dragging: self.move_dragged = True self.viewState.transform = M.translate(*delta).dot(self.viewState.transform) needs_update = True elif (event.buttons() & QtCore.Qt.MiddleButton): delta = -10 * delta_px.y() self.wheelEvent(QtGui.QWheelEvent(event.pos(),delta, event.buttons(), event.modifiers())) needs_update = True elif not self.move_dragging and not self.mwemu: if self.interactionDelegate is not None: self.interactionDelegate.mouseMoveEvent(event) needs_update = True if needs_update: self.update() def mouseReleaseEvent(self, event): if event.button() == MOVE_MOUSE_BUTTON and self.move_dragging: self.move_dragging = False return # Middle mouse button events are mapped to mouse wheel elif event.button() == QtCore.Qt.MiddleButton: self.mwemu = False return if not self.move_dragged and not self.move_dragging and not self.mwemu: if self.interactionDelegate is not None: self.interactionDelegate.mouseReleaseEvent(event) self.update() def wheelEvent(self, event): """ :param event: :type event: QtGui.MouseWheelEvent :return: """ if not event.modifiers(): step = event.delta()/120.0 sf = 1.1 ** step fixed_center_dot(self.viewState, M.scale(sf), QPoint_to_pair(event.pos())) else: if self.interactionDelegate: self.interactionDelegate.mouseWheelEvent(event) def initializeGL(self): GL.glClearColor(0,0,0,1) self.gls.initializeGL() GL.glGetError() GL.glEnable(GL.GL_BLEND) # Additive Blending GL.glBlendFunc(GL.GL_SRC_ALPHA, GL.GL_ONE) self.reinit() if self.interactionDelegate is not None and self.interactionDelegate.overlay is not None: self.interactionDelegate.overlay.initializeGL(self.gls) def reinit(self): pass def paintGL(self): GL.glClear(GL.GL_COLOR_BUFFER_BIT, GL.GL_DEPTH_BUFFER_BIT) self.render() def render(self): if self.interactionDelegate and self.interactionDelegate.overlay: self.interactionDelegate.overlay.render(self.viewState) def resizeGL(self, width, height): if width == 0 or height == 0: return GL.glViewport(0, 0, width, height) self.viewState.resize(self.width(), self.height()) # Allocate a new image buffer #self.image = NPBackedImage(self.width(), self.height()) #super(BoardViewWidget, self).resizeEvent(event) def isModified(self): return self.modified