Browse Source

bounding box for move

tb-init-ui-render
Taylor Bockman 5 years ago
parent
commit
f538fc648b
  1. 4
      clusterview/exceptions.py
  2. 77
      clusterview/mode_handlers.py
  3. 129
      clusterview/opengl_widget.py
  4. 23
      main_window.py

4
clusterview/exceptions.py

@ -42,9 +42,9 @@ def handle_exceptions(func):
def my_qt_func(): def my_qt_func():
raises SomeException raises SomeException
""" """
def wrapped(self, *args, **kwargs): def wrapped(*args, **kwargs):
try: try:
return func(self, *args, **kwargs) return func(*args, **kwargs)
except Exception as e: except Exception as e:
error_dialog = QErrorMessage() error_dialog = QErrorMessage()
error_dialog.showMessage(str(e)) error_dialog.showMessage(str(e))

77
clusterview/mode_handlers.py

@ -1,5 +1,9 @@
from PyQt5.QtCore import QEvent, Qt
from .mode import Mode from .mode import Mode
from .opengl_widget import set_current_points, set_drawing_event from .opengl_widget import (set_current_points, set_drawing_event,
set_move_bb_top_left, set_move_bb_bottom_right,
reset_move_bbs)
from .points import PointSet from .points import PointSet
# Size of point for drawing # Size of point for drawing
@ -9,6 +13,11 @@ __POINT_SIZE = 8
# nature of state management in OpenGL. # nature of state management in OpenGL.
__point_set = PointSet(__POINT_SIZE) __point_set = PointSet(__POINT_SIZE)
# Module level flag for left click events (used to detect a left
# click hold drag)
__left_click_flag = False
def __refresh_point_list(ctx): def __refresh_point_list(ctx):
""" """
Refreshes the point list display. Refreshes the point list display.
@ -37,16 +46,19 @@ def __handle_add_point(ctx, event):
""" """
global __point_set global __point_set
# No attribute at the moment. if (event.button() == Qt.LeftButton and
__point_set.add_point(event.x(), event.y()) event.type() == QEvent.MouseButtonPress):
__refresh_point_list(ctx) # No attribute at the moment.
__point_set.add_point(event.x(), event.y())
set_drawing_event(event) __refresh_point_list(ctx)
set_current_points(__point_set)
ctx.opengl_widget.update() set_drawing_event(event)
ctx.point_list_widget.update() set_current_points(__point_set)
ctx.opengl_widget.update()
ctx.point_list_widget.update()
def __handle_edit_point(ctx, event): def __handle_edit_point(ctx, event):
@ -67,23 +79,56 @@ def __handle_edit_point(ctx, event):
ctx.update() ctx.update()
# after this remove the point from the list # after this remove the point from the list
def __handle_move_points(ctx, event): def __handle_move_points(ctx, event):
# TODO: Should move the associated points in the list to the new location. global __left_click_flag
# Store list of old points that are captured
set_drawing_event(event) set_drawing_event(event)
ctx.update()
if event.button() == Qt.LeftButton:
__left_click_flag = True
# This if statement block is used to set the bounding box for
# drawing and call the selection procedure.
if __left_click_flag and event.type() == QEvent.MouseButtonPress:
set_move_bb_top_left(event.x(), event.y())
elif __left_click_flag and event.type() == QEvent.MouseMove:
set_move_bb_bottom_right(event.x(), event.y())
elif __left_click_flag and event.type() == QEvent.MouseButtonRelease:
__left_click_flag = False
# Final bottom right corner point
set_move_bb_bottom_right(event.x(), event.y())
# Call the selection procedure
# TODO
# Satisfy the post condition by resetting the bounding box
reset_move_bbs()
# Find and move all points from the old list to their new locations # Find and move all points from the old list to their new locations
ctx.opengl_widget.update()
def __handle_delete_point(ctx, event): def __handle_delete_point(ctx, event):
set_drawing_event(event) if (event.button() == Qt.LeftButton and
event.type() == QEvent.MouseButtonPress):
__point_set.remove_point(event.x(), event.y()) set_drawing_event(event)
__refresh_point_list(ctx) __point_set.remove_point(event.x(), event.y())
ctx.opengl_widget.update() __refresh_point_list(ctx)
ctx.point_list_widget.update()
ctx.opengl_widget.update()
ctx.point_list_widget.update()
# Simple dispatcher to make it easy to dispatch the right mode # Simple dispatcher to make it easy to dispatch the right mode
# function when the OpenGL window is clicked. # function when the OpenGL window is clicked.

129
clusterview/opengl_widget.py

@ -8,26 +8,31 @@ To be clear, the actual widget is defined in the UI
generated code - `clusterview_ui.py`. The functions generated code - `clusterview_ui.py`. The functions
here are imported as overrides to the OpenGL functions of here are imported as overrides to the OpenGL functions of
that widget. that widget.
It should be split up into a few more separate files eventually...
Probably even into it's own module folder.
""" """
from enum import Enum from enum import Enum
from OpenGL.GL import (glBegin, glClearColor, glColor4f, glEnable, from OpenGL.GL import (glBegin, glClearColor, glColor4f, glEnable,
glEnd, GL_LIGHT0, GL_LIGHTING, GL_POINTS, glEnd, GL_LIGHT0, GL_LIGHTING, GL_LINE_LOOP, GL_POINTS,
glPointSize, glVertex3f, glViewport) glPointSize, glVertex3f, glViewport)
from .exceptions import InvalidModeError, InvalidStateError from .exceptions import handle_exceptions, InvalidModeError, InvalidStateError
from .mode import Mode from .mode import Mode
from .points import PointSet from .points import PointSet
class Color(Enum): class Color(Enum):
BLUE = 0 BLUE = 0
BLACK = 1
# A simple map from Color -> RGBA 4-Tuple # A simple map from Color -> RGBA 4-Tuple
# Note: The color values in the tuple are not RGB, but # Note: The color values in the tuple are not RGB, but
# rather OpenGL percentage values for RGB. # rather OpenGL percentage values for RGB.
COLOR_TO_RGBA = { COLOR_TO_RGBA = {
Color.BLUE: (0, 0.5, 1.0, 0.0) Color.BLUE: (0, 0.5, 1.0, 0.0),
Color.BLACK: (0.0, 0.0, 0.0, 0.0)
} }
# Constants set based on the size of the window. # Constants set based on the size of the window.
@ -35,6 +40,11 @@ __BOTTOM_LEFT = (0, 0)
__WIDTH = None __WIDTH = None
__HEIGHT = None __HEIGHT = None
# State variables for a move selection bounding box.
# There are always reset to None after a selection has been made.
__move_bb_top_left = None
__move_bb_bottom_right = None
# Module-global state variables for our drawing # Module-global state variables for our drawing
# state machine. # state machine.
# #
@ -117,6 +127,38 @@ def set_drawing_event(event):
__current_event = event __current_event = event
def set_move_bb_top_left(x, y):
"""
Called to set the move bounding box's top left corner.
@param x The x-coordinate.
@param y The y-coordinate.
"""
global __move_bb_top_left
__move_bb_top_left = (x, y)
def set_move_bb_bottom_right(x, y):
"""
Called to set the move bounding box's bottom right corner.
@param x The x-coordinate.
@param y The y-coordinate.
"""
global __move_bb_bottom_right
__move_bb_bottom_right = (x, y)
def reset_move_bbs():
global __move_bb_top_left
global __move_bb_bottom_right
__move_bb_top_left = None
__move_bb_bottom_right = None
def initialize_gl(): def initialize_gl():
""" """
Initializes the OpenGL context on the Window. Initializes the OpenGL context on the Window.
@ -144,6 +186,7 @@ def resize_gl(w, h):
__HEIGHT = __current_context.height() __HEIGHT = __current_context.height()
@handle_exceptions
def paint_gl(): def paint_gl():
""" """
Stock PaintGL function from OpenGL that switches Stock PaintGL function from OpenGL that switches
@ -155,10 +198,9 @@ def paint_gl():
raise InvalidStateError("Event must exist for ADD, EDIT, MOVE, " + raise InvalidStateError("Event must exist for ADD, EDIT, MOVE, " +
"and DELETE") "and DELETE")
if (__current_mode in [Mode.ADD, Mode.EDIT, Mode.MOVE, Mode.DELETE] and if (__current_mode in [Mode.ADD, Mode.EDIT, Mode.DELETE] and
__current_points is None): __current_points is None):
raise InvalidStateError("Points must exist for ADD, EDIT, MOVE, " + return
"and DELETE")
if __current_mode is Mode.ADD or __current_mode is Mode.DELETE: if __current_mode is Mode.ADD or __current_mode is Mode.DELETE:
# Note that drawing the points doesn't require a bounding box or # Note that drawing the points doesn't require a bounding box or
@ -167,10 +209,29 @@ def paint_gl():
# is the same as adding a point since we just draw what is in # is the same as adding a point since we just draw what is in
# the point set. # the point set.
draw_points(__current_points, Color.BLUE) draw_points(__current_points, Color.BLUE)
elif __current_mode is Mode.EDIT: elif __current_mode is Mode.EDIT:
raise NotImplementedError("Drawing for EDIT not implemented.") raise NotImplementedError("Drawing for EDIT not implemented.")
elif __current_mode is Mode.MOVE: elif __current_mode is Mode.MOVE:
raise NotImplementedError("Drawing for MOVE not implemented.") # We have to repeatedly draw the points while we are showing the
# move box.
if __current_points is not None:
draw_points(__current_points, Color.BLUE)
draw_selection_box(Color.BLACK)
if __move_bb_top_left is None and __move_bb_bottom_right is None:
# Currently this fires all the time - not great. Needs to only fire
# when, additionally, we have a selection chosen based on the box
# calculated in the mode handlers.
print("FIRE THE MOVE STUFF")
# Once the selection boxes go to None begin the highlight selected
# points procedure. This will store a point list of selected points
# highlight them, etc.
# Once thats done moving points will be difficult but do-able.
elif __current_mode is Mode.DELETE: elif __current_mode is Mode.DELETE:
raise NotImplementedError("Drawing for DELETE not implemented.") raise NotImplementedError("Drawing for DELETE not implemented.")
@ -199,6 +260,60 @@ def __clamp_y(y):
return y_w return y_w
def draw_selection_box(color):
"""
When the move bounding box state is populated and the mode is set
to MODE.Move this function will draw the selection bounding box.
@param color The color Enum.
"""
global __current_context
if __current_context is None:
raise InvalidStateError("Drawing context must be set before setting " +
"drawing mode")
if not isinstance(color, Color):
raise ValueError("Color must exist in the Color enumeration")
if __move_bb_top_left is None or __move_bb_bottom_right is None:
# Nothing to draw.
return
ct = COLOR_TO_RGBA[color]
glViewport(0, 0, __WIDTH, __HEIGHT)
# Top right corner has the same x as the bottom right
# and same y as the top left.
top_right_corner = (__move_bb_bottom_right[0], __move_bb_top_left[1])
# Bottom left corner has the same x as the top left and
# same y as the bottom right.
bottom_left_corner = (__move_bb_top_left[0], __move_bb_bottom_right[1])
glBegin(GL_LINE_LOOP)
glColor4f(ct[0], ct[1], ct[2], ct[3])
glVertex3f(__clamp_x(__move_bb_top_left[0]),
__clamp_y(__move_bb_top_left[1]),
0.0)
glVertex3f(__clamp_x(top_right_corner[0]),
__clamp_y(top_right_corner[1]),
0.0)
glVertex3f(__clamp_x(__move_bb_bottom_right[0]),
__clamp_y(__move_bb_bottom_right[1]),
0.0)
glVertex3f(__clamp_x(bottom_left_corner[0]),
__clamp_y(bottom_left_corner[1]),
0.0)
glEnd()
def draw_points(point_set, color): def draw_points(point_set, color):
""" """
Simple point drawing function. Simple point drawing function.

23
main_window.py

@ -32,6 +32,10 @@ class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow):
# to OpenGL coordinates properly. # to OpenGL coordinates properly.
set_drawing_context(self.opengl_widget) set_drawing_context(self.opengl_widget)
# Enables mouse tracking on the viewport so mouseMoveEvents are
# tracked and fired properly.
self.opengl_widget.setMouseTracking(True)
#----------------------------------------------- #-----------------------------------------------
# OpenGL Graphics Handlers are set # OpenGL Graphics Handlers are set
# here and defined in clusterview.opengl_widget. # here and defined in clusterview.opengl_widget.
@ -52,6 +56,8 @@ class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow):
# Override handler for mouse press so we can draw points based on # Override handler for mouse press so we can draw points based on
# the OpenGL coordinate system inside of the OpenGL Widget. # the OpenGL coordinate system inside of the OpenGL Widget.
self.opengl_widget.mousePressEvent = self.__ogl_click_dispatcher 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 # Mode changers - these will be used to signal the action in the
@ -98,12 +104,11 @@ class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow):
""" """
Mode dispatcher for click actions on the OpenGL widget. Mode dispatcher for click actions on the OpenGL widget.
""" """
if self.__mode is Mode.OFF:
raise InvalidModeError(Mode.OFF) if self.__mode is not Mode.OFF:
# Map from Mode -> function
# Map from Mode -> function # where the function is a handler for the
# where the function is a handler for the # OpenGL event. The context passed to these functions allows
# OpenGL event. The context passed to these functions allows # them to modify on screen widgets such as the QOpenGLWidget and
# them to modify on screen widgets such as the QOpenGLWidget and # QListWidget.
# QListWidget. MODE_HANDLER_MAP[self.__mode](self, event)
MODE_HANDLER_MAP[self.__mode](self, event)

Loading…
Cancel
Save