from enum import Enum from functools import partial import os from PyQt5.QtCore import Qt from PyQt5.QtGui import QCursor from PyQt5 import QtWidgets, uic from clusterview.exceptions import handle_exceptions, InvalidModeError from clusterview.mode import Mode from clusterview.mode_handlers import MODE_HANDLER_MAP, ogl_keypress_handler from clusterview.opengl_widget import (clear_selection, initialize_gl, paint_gl, resize_gl, set_drawing_mode, set_drawing_context) from clusterview.point_list_widget import item_click_handler from clusterview_ui import Ui_MainWindow class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow): """ A wrapper class for handling creating a window based on the `clusterview_ui.py` code generated from `clusterview.ui`. """ __mode = Mode.OFF def __init__(self, parent=None): super(MainWindow, self).__init__(parent) self.setupUi(self) # We only need to set the context in our OpenGL state machine # wrapper once here since the window is fixed size. # If we allow resizing of the window, the context must be updated # each resize so that coordinates are converted from screen (x, y) # to OpenGL coordinates properly. set_drawing_context(self.opengl_widget) # Enables mouse tracking on the viewport so mouseMoveEvents are # tracked and fired properly. self.opengl_widget.setMouseTracking(True) # Here we partially apply the key press handler with self to # create a new function that only expects the event `keyPressEvent` # expects. In this way, we've snuck the state of the opengl_widget # into the function so that we can modify it as we please. self.opengl_widget.keyPressEvent = partial(ogl_keypress_handler, self) # Same story here but this time with the itemClicked event # so that when an element is clicked on in the point list it will # highlight. self.point_list_widget.itemClicked.connect(partial(item_click_handler, self)) #----------------------------------------------- # OpenGL Graphics Handlers are set # here and defined in clusterview.opengl_widget. #----------------------------------------------- self.opengl_widget.initializeGL = initialize_gl self.opengl_widget.paintGL = paint_gl self.opengl_widget.resizeGL = resize_gl # ------------------------------------- # UI Handlers # ------------------------------------- self.action_add_points.triggered.connect(self.__add_points) self.action_edit_points.triggered.connect(self.__edit_points) self.action_delete_points.triggered.connect(self.__delete_points) self.action_move_points.triggered.connect(self.__move_points) self.action_solve.triggered.connect(self.__solve_launcher) # Override handler for mouse press so we can draw points based on # the OpenGL coordinate system inside of the OpenGL Widget. self.opengl_widget.mousePressEvent = self.__ogl_click_dispatcher self.opengl_widget.mouseMoveEvent = self.__ogl_click_dispatcher self.opengl_widget.mouseReleaseEvent = self.__ogl_click_dispatcher #----------------------------------------------------------------- # Mode changers - these will be used to signal the action in the # OpenGL Widget. #----------------------------------------------------------------- def __off_mode(self): self.opengl_widget.setCursor(QCursor(Qt.CursorShape.ArrowCursor)) self.status_bar.showMessage("") clear_selection() self.opengl_widget.update() def __add_points(self): self.__mode = Mode.ADD set_drawing_mode(self.__mode) self.opengl_widget.setCursor(QCursor(Qt.CursorShape.CrossCursor)) self.status_bar.showMessage("ADD MODE") clear_selection() self.opengl_widget.update() def __edit_points(self): self.__mode = Mode.EDIT set_drawing_mode(self.__mode) self.opengl_widget.setCursor(QCursor(Qt.CursorShape.CrossCursor)) self.status_bar.showMessage("EDIT MODE") clear_selection() self.opengl_widget.update() def __delete_points(self): self.__mode = Mode.DELETE set_drawing_mode(self.__mode) self.opengl_widget.setCursor(QCursor(Qt.CursorShape.PointingHandCursor)) self.status_bar.showMessage("DELETE MODE") clear_selection() self.opengl_widget.update() def __move_points(self): self.__mode = Mode.MOVE set_drawing_mode(self.__mode) self.opengl_widget.setCursor(QCursor(Qt.CursorShape.SizeAllCursor)) self.status_bar.showMessage("MOVE MODE - PRESS ESC OR SWITCH MODES TO "+ "CANCEL SELECTION") clear_selection() self.opengl_widget.update() @property def mode(self): """ Function designed to be used from a context to get the current mode. """ return self.__mode @mode.setter def mode(self, mode): self.__mode = mode @handle_exceptions def __solve_launcher(self): """ Launched the solve menu. This function will call into a subclass of the solve dialog widget from the UI. TODO: Write the subclass once you know the parameters for the solve window. """ print("LAUNCHING SOLVE DIALOG...") @handle_exceptions def __ogl_click_dispatcher(self, event): """ Mode dispatcher for click actions on the OpenGL widget. """ if self.__mode is not Mode.OFF: # Map from Mode -> function # where the function is a handler for the # OpenGL event. The context passed to these functions allows # them to modify on screen widgets such as the QOpenGLWidget and # QListWidget. MODE_HANDLER_MAP[self.__mode](self, event) else: # Go back to the base state self.__off_mode()