From 6f9829f4f82d0f4fbb862f9d227b527658df286c Mon Sep 17 00:00:00 2001 From: Daniel Carta <79732052+immortaldan10@users.noreply.github.com> Date: Thu, 25 Jul 2024 10:34:40 -0400 Subject: [PATCH] Added comments to the button editor --- buttonEditor.py | 80 ++++++++++++++++++++++++++++++++++++------------- 1 file changed, 59 insertions(+), 21 deletions(-) diff --git a/buttonEditor.py b/buttonEditor.py index 86180d4..cb53e6a 100644 --- a/buttonEditor.py +++ b/buttonEditor.py @@ -5,12 +5,19 @@ from PySide6.QtWidgets import QApplication, QLabel, QMainWindow, QPushButton, QV from PySide6.QtGui import QPixmap, QPainter, QPen, QColor from PySide6.QtCore import Qt, QPoint, QRect +''' +This window is the button editor +The button editor takes the path that is made in the path planner and lets the user fine tune the auto by adding button inputs, changing timing, etc... +''' + class ButtonEditor(QMainWindow): def __init__(self, pathPlanner): super().__init__() + # Initialization self.setWindowTitle("Button Editor") self.pathPlanner = pathPlanner + # Background / Field setup self.imageLabel = QLabel(self) scriptDir = os.path.dirname(os.path.abspath(__file__)) imagePath = os.path.join(scriptDir, "images", "Field.png") @@ -21,58 +28,66 @@ class ButtonEditor(QMainWindow): else: self.imageLabel.setPixmap(self.pixmap) + # Buttons at the top of the screen self.pathPlannerButton = QPushButton("Main Window") self.pathPlannerButton.clicked.connect(self.showPathPlanner) self.buttonEditorButton = QPushButton("Button Editor") self.buttonEditorButton.clicked.connect(self.showButtonEditor) + # Button layouts buttonLayout = QHBoxLayout() buttonLayout.addWidget(self.pathPlannerButton) buttonLayout.addWidget(self.buttonEditorButton) + # Adding the button layout to the main layout layout = QVBoxLayout() layout.addLayout(buttonLayout) layout.addWidget(self.imageLabel) + # Adding the time text to the layout self.timeLabel = QLabel("(0:00 / 0:15 sec)", self) layout.addWidget(self.timeLabel, alignment=Qt.AlignCenter) + # Defining the overall layout self.rectanglesWidget = QWidget() self.rectanglesLayout = QHBoxLayout() self.rectanglesLayout.setSpacing(0) self.rectanglesLayout.setContentsMargins(40, 0, 0, 0) self.rectanglesWidget.setLayout(self.rectanglesLayout) layout.addWidget(self.rectanglesWidget) - container = QWidget() container.setLayout(layout) self.setCentralWidget(container) + # Resize the window to all of the layouts and widgets added self.resize(self.pixmap.width(), self.pixmap.height() + 250) # Variables - self.matchLength = 15 - self.TPS = 50 - self.matchTicks = self.matchLength * self.TPS - self.displayTickResolution = 6.25 - self.displayTicks = round(self.matchTicks / self.displayTickResolution) + self.matchLength = 15 # How many seconds the auto lasts (it's always 15) + self.TPS = 50 # Ticks per second (the robot runs at 50 tps) + self.matchTicks = self.matchLength * self.TPS # The amount of ticks in a match + self.displayTickResolution = 6.25 # The number of ticks to divide the match ticks by + self.displayTicks = round(self.matchTicks / self.displayTickResolution) # How many ticks to show the user - self.currentFrame = 1 - self.keyFrameData = [{"isNode": False, "isButton": False} for _ in range(self.displayTicks)] + self.currentFrame = 1 # The frame that is currently selected + self.keyFrameData = [{"isNode": False, "isButton": False} for _ in range(self.displayTicks)] # Dictionary for every tick including if that tick is a node frame or a button frame self.updateKeyFrameData() - self.displayFrames = list(range(1, self.displayTicks + 1)) + self.displayFrames = list(range(1, self.displayTicks + 1)) # The list of display frames - self.currentTime = [i * self.matchLength / (self.displayTicks - 1) for i in range(self.displayTicks)] + self.currentTime = [i * self.matchLength / (self.displayTicks - 1) for i in range(self.displayTicks)] # Divides the amount of frames by the match length to put a time for every frame - num_nodes = 2 + # Setting up the nodes from the path planner + num_nodes = 2 # The number of nodes in the path planner node_indices = np.linspace(0, self.displayTicks - 1, num_nodes, dtype=int) for idx in node_indices: self.keyFrameData[idx]["isNode"] = True + # Initialization self.setupFrames() self.updateRectangles() self.updateTimeLabel() + # Updating the key frames with data from the path planner def updateKeyFrameData(self): num_nodes = len(self.pathPlanner.coordinates) if self.pathPlanner else 0 self.keyFrameData = [{"isNode": False, "isButton": False} for _ in range(self.displayTicks)] @@ -82,34 +97,40 @@ class ButtonEditor(QMainWindow): for idx in node_indices: self.keyFrameData[idx]["isNode"] = True + # Resizing the rectangles and updating the time on call def resizeEvent(self, event): super().resizeEvent(event) self.updateRectangles() self.updateTimeLabel() + # Setup for the display frames def setupFrames(self): self.displayFrames = list(range(1, self.displayTicks + 1)) - print(len(self.keyFrameData)) - print(len(self.displayFrames)) + # Move to the button editor window def showButtonEditor(self): self.show() if self.pathPlanner: self.pathPlanner.hide() + # Move to the path planner window def showPathPlanner(self): self.hide() if self.pathPlanner: self.pathPlanner.show() + # Updates the scene with all of the data from the path planner and draw the scene def updateScene(self): - self.updateKeyFrameData() + self.updateKeyFrameData() # Make sure key frame data is up to date + + # Draw the field self.pixmap = QPixmap(os.path.join(os.path.dirname(os.path.abspath(__file__)), "images", "Field.png")) painter = QPainter(self.pixmap) painter.setPen(Qt.NoPen) painter.setBrush(Qt.white) + # Draw the nodes from the path planner onto the button editor if self.pathPlanner and hasattr(self.pathPlanner, 'coordinates'): for i, (x, y) in enumerate(self.pathPlanner.coordinates): nodeRect = QRect(x - self.pathPlanner.nodeSize // 6, y - self.pathPlanner.nodeSize // 6, @@ -117,17 +138,19 @@ class ButtonEditor(QMainWindow): painter.drawEllipse(nodeRect) painter.drawText(nodeRect, Qt.AlignCenter, str(i + 1)) + # Draw points on the bezier curve in between the nodes (Where the robot is going to go) if len(self.pathPlanner.coordinates) > 1: - total_points = 60 + total_points = 60 # The amount of points to draw points_per_curve = total_points // len(self.pathPlanner.controlPoints) remaining_points = total_points % len(self.pathPlanner.controlPoints) + # Placing the points and nodes for i, controlPair in enumerate(self.pathPlanner.controlPoints): if i < len(self.pathPlanner.coordinates) - 1: start = QPoint(self.pathPlanner.coordinates[i][0], self.pathPlanner.coordinates[i][1]) end = QPoint(self.pathPlanner.coordinates[i + 1][0], self.pathPlanner.coordinates[i + 1][1]) - pen = QPen(Qt.yellow if self.keyFrameData[i]["isNode"] else Qt.white) # Set color based on isNode + pen = QPen(Qt.yellow if self.keyFrameData[i]["isNode"] else Qt.white) pen.setWidth(2) painter.setPen(pen) @@ -135,10 +158,12 @@ class ButtonEditor(QMainWindow): if i < remaining_points: num_points += 1 + # Set up the points on the bezier curve for t in np.linspace(0, 1, num_points): point = self.pointOnBezierCurve(start, controlPair[0], controlPair[1], end, t) painter.drawEllipse(point, 2, 2) + # Draw every point on the bezier curve with the rotation of the robot at that point if i == self.currentFrame - 1: t = (self.currentFrame - 1) / (num_points - 1) point = self.pointOnBezierCurve(start, controlPair[0], controlPair[1], end, t) @@ -147,14 +172,18 @@ class ButtonEditor(QMainWindow): angle = self.interpolateAngle(start_angle, end_angle, t) self.drawRobot(painter, point, angle) - painter.end() + painter.end() # If you don't end the painter the app crashes self.imageLabel.setPixmap(self.pixmap) self.updateRectangles() + # Finds the angle between two angles + # This is used to find the angles of all of the points in between the nodes def interpolateAngle(self, start_angle, end_angle, t): diff = (end_angle - start_angle + 180) % 360 - 180 return start_angle + diff * t + # Draw where the robot is at the current frame + # DOES NOT WORK AT THE MOMENT def drawRobot(self, painter, position, angle): side_length = self.pathPlanner.nodeSize half_side = side_length / 2 @@ -173,26 +202,31 @@ class ButtonEditor(QMainWindow): painter.restore() + # Function for drawing the points on the bezier curve (I dont get the math tbh) def pointOnBezierCurve(self, start, control1, control2, end, t): x = (1-t)**3 * start.x() + 3*(1-t)**2*t * control1.x() + 3*(1-t)*t**2 * control2.x() + t**3 * end.x() y = (1-t)**3 * start.y() + 3*(1-t)**2*t * control1.y() + 3*(1-t)*t**2 * control2.y() + t**3 * end.y() return QPoint(int(x), int(y)) + # Update the frames on the bottom of the screen that the user interacts with def updateRectangles(self): + # Add the key frame widget for i in reversed(range(self.rectanglesLayout.count())): widgetToRemove = self.rectanglesLayout.itemAt(i).widget() self.rectanglesLayout.removeWidget(widgetToRemove) widgetToRemove.setParent(None) + #Set the key frames width window_width = self.rectanglesWidget.width() if self.keyFrameData: rect_width = window_width / len(self.keyFrameData) - rect_height = 100 + rect_height = 100 # Adjustable key frame height for index, frame in enumerate(self.keyFrameData): rectWidget = QWidget() rectWidget.setFixedSize(rect_width, rect_height) + # Set the colors of the frames if frame["isNode"]: rectWidget.setStyleSheet("background-color: yellow;") else: @@ -204,22 +238,26 @@ class ButtonEditor(QMainWindow): rectWidget.mousePressEvent = lambda event, idx=index: self.rectangleClicked(idx) self.rectanglesLayout.addWidget(rectWidget) + # Set the current frame to the frame that the user clicked def rectangleClicked(self, index): - self.currentFrame = index + 1 + self.currentFrame = index + 1 # Setting the frame self.updateRectangles() self.updateScene() self.updateTimeLabel() + # Set the current frame to be red clicked_widget = self.rectanglesLayout.itemAt(index).widget() clicked_widget.setStyleSheet("background-color: red;") + # Update the time depending on the current frame def updateTimeLabel(self): - current_time = self.currentTime[self.currentFrame - 1] - minutes = int(current_time // 60) + current_time = self.currentTime[self.currentFrame - 1] # Sets the time to the current time + minutes = int(current_time // 60) seconds = int(current_time % 60) milliseconds = int((current_time % 1) * 1000) self.timeLabel.setText(f"{minutes}:{seconds:02d}.{milliseconds:03d} / {self.matchLength:.3f} sec") +# App initialization if __name__ == "__main__": app = QApplication(sys.argv) window = ButtonEditor(None)