From 99a298ec9b5c6f5c2ccde6e1a0d18190edce94fc Mon Sep 17 00:00:00 2001 From: Daniel Carta <79732052+immortaldan10@users.noreply.github.com> Date: Fri, 26 Jul 2024 13:59:15 -0400 Subject: [PATCH] HUGE changes - Added an about page that shows readme - Added even more comments - Added a playback feature - 100x the pain and suffering! --- .DS_Store | Bin 6148 -> 6148 bytes README.md | 21 ++++---- about.py | 43 +++++++++++++++++ buttonEditor.py | 125 +++++++++++++++++++++++++++++++++++++----------- main.py | 66 ++++++++++++++++++------- 5 files changed, 198 insertions(+), 57 deletions(-) create mode 100644 about.py diff --git a/.DS_Store b/.DS_Store index 5cda457be8541b7de84b90156c0ed8d3cfe5b16e..56632b4b8c96a961ee04463ead20d48924d9026a 100644 GIT binary patch delta 35 rcmZoMXfc@J&&a+pU^gQp`(z%b^2wW-er>+SY{)XPA#yW2$6tN`)@2N6 delta 85 zcmZoMXfc@J&&atkU^gQp=VTtHazPG;G=@xu9EMZ|J%$p73Wk!+DNK@#ip&hf3`Gpd io;mr+NjdpR3=9kcK 1: + self.currentFrame -= 1 + elif event.key() == Qt.Key_E: + self.paused = not self.paused + if self.paused: + self.timer.stop() + else: + self.timer.start(int(1000 // (self.TPS * self.playbackSpeed))) + + print(self.paused) + + if old_frame != self.currentFrame: + self.updateRectangles() + self.updateScene() + self.updateTimeLabel() # Updating the key frames with data from the path planner def updateKeyFrameData(self): @@ -170,6 +200,25 @@ class ButtonEditor(QMainWindow): start_angle = self.pathPlanner.nodeAngles[i] if i < len(self.pathPlanner.nodeAngles) else 0 end_angle = self.pathPlanner.nodeAngles[i + 1] if i + 1 < len(self.pathPlanner.nodeAngles) else 0 angle = self.interpolateAngle(start_angle, end_angle, t) + + total_frames = self.displayTicks + frame_index = self.currentFrame - 1 + if 0 <= frame_index < total_frames: + overall_t = frame_index / (total_frames - 1) + num_segments = len(self.pathPlanner.coordinates) - 1 + segment_index = min(int(overall_t * num_segments), num_segments - 1) + segment_t = (overall_t * num_segments) - segment_index + + start = QPoint(self.pathPlanner.coordinates[segment_index][0], self.pathPlanner.coordinates[segment_index][1]) + end = QPoint(self.pathPlanner.coordinates[segment_index + 1][0], self.pathPlanner.coordinates[segment_index + 1][1]) + control1 = self.pathPlanner.controlPoints[segment_index][0] + control2 = self.pathPlanner.controlPoints[segment_index][1] + + point = self.pointOnBezierCurve(start, control1, control2, end, segment_t) + + start_angle = self.pathPlanner.nodeAngles[segment_index] if segment_index < len(self.pathPlanner.nodeAngles) else 0 + end_angle = self.pathPlanner.nodeAngles[segment_index + 1] if segment_index + 1 < len(self.pathPlanner.nodeAngles) else 0 + angle = self.interpolateAngle(start_angle, end_angle, segment_t) self.drawRobot(painter, point, angle) painter.end() # If you don't end the painter the app crashes @@ -210,33 +259,40 @@ class ButtonEditor(QMainWindow): # 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 # Adjustable key frame height - for index, frame in enumerate(self.keyFrameData): - rectWidget = QWidget() - rectWidget.setFixedSize(rect_width, rect_height) + # Clear existing rectangles + while self.rectanglesLayout.count(): + item = self.rectanglesLayout.takeAt(0) + widget = item.widget() + if widget: + widget.deleteLater() + + # Calculate the width of each rectangle + total_width = self.width() - 80 + rect_width = max(1, total_width // self.displayTicks) + + # Create new rectangles + for i in range(self.displayTicks): + rect = QWidget() + rect.setFixedSize(rect_width, 100) - # Set the colors of the frames - if frame["isNode"]: - rectWidget.setStyleSheet("background-color: yellow;") + # Set the color based on frame type + if i == self.currentFrame - 1: + color = "red" + elif self.keyFrameData[i]["isNode"]: + color = "yellow" + elif self.keyFrameData[i]["isButton"]: + color = "blue" else: - if index % 2 == 0: - rectWidget.setStyleSheet("background-color: #ADD8E6;") - else: - rectWidget.setStyleSheet("background-color: #00008B;") + color = "#ADD8E6" if i % 2 == 0 else "#00008B" + rect.setStyleSheet(f"background-color: {color};") - rectWidget.mousePressEvent = lambda event, idx=index: self.rectangleClicked(idx) - self.rectanglesLayout.addWidget(rectWidget) + # Connect the click event + rect.mousePressEvent = lambda event, index=i: self.rectangleClicked(index) + self.rectanglesLayout.addWidget(rect) + + # Force layout update + self.rectanglesWidget.updateGeometry() + self.rectanglesLayout.update() # Set the current frame to the frame that the user clicked def rectangleClicked(self, index): @@ -257,6 +313,19 @@ class ButtonEditor(QMainWindow): milliseconds = int((current_time % 1) * 1000) self.timeLabel.setText(f"{minutes}:{seconds:02d}.{milliseconds:03d} / {self.matchLength:.3f} sec") + # The auto playback + def advanceFrame(self): + if not self.paused: + if self.currentFrame < len(self.displayFrames): + self.currentFrame += 1 + else: + self.currentFrame = 1 # Loop back to the start + + # Update + self.updateRectangles() + self.updateScene() + self.updateTimeLabel() + # App initialization if __name__ == "__main__": app = QApplication(sys.argv) diff --git a/main.py b/main.py index 5df2c82..381ae93 100644 --- a/main.py +++ b/main.py @@ -5,79 +5,105 @@ from PySide6.QtWidgets import QApplication, QLabel, QMainWindow, QPushButton, QV from PySide6.QtGui import QPixmap, QMouseEvent, QPainter, QPen, QColor, QPainterPath, QPolygon, QFont, QKeyEvent from PySide6.QtCore import Qt, QPoint, QRect -from buttonEditor import ButtonEditor +from buttonEditor import ButtonEditor # Import the button editor +from about import AboutWindow + +''' +This is the path planner window +The path planner lets the user add nodes and control points to let them change the bezier curves and the path of the robot during the auto +''' class PathPlanner(QMainWindow): def __init__(self): super().__init__() + self.setWindowTitle("Path Planner") # Set the window title - self.setWindowTitle("Path Planner") - - self.coordinates = np.empty((0, 2), dtype=int) + # Set up the arrays + self.coordinates = np.empty((0, 2), dtype=int) # Make an empty array for the coordinates of objects self.controlPoints = [] self.rotationHandles = [] self.nodeAngles = [] + # Find the field png and then set the background to that png self.imageLabel = QLabel(self) - scriptDir = os.path.dirname(os.path.abspath(__file__)) imagePath = os.path.join(scriptDir, "images", "Field.png") self.pixmap = QPixmap(imagePath) - + if self.pixmap.isNull(): self.imageLabel.setText(f"Image not found at: {imagePath}") else: self.imageLabel.setPixmap(self.pixmap) + # Buttons at the top of the screen self.mainWindowButton = QPushButton("Main Window") self.mainWindowButton.clicked.connect(self.showMainWindow) self.buttonEditorButton = QPushButton("Button Editor") self.buttonEditorButton.clicked.connect(self.showButtonEditor) + self.aboutButton = QPushButton("About") + self.aboutButton.clicked.connect(self.showAbout) + # Button layouts buttonLayout = QHBoxLayout() buttonLayout.addWidget(self.mainWindowButton) buttonLayout.addWidget(self.buttonEditorButton) + buttonLayout.addWidget(self.aboutButton) + # Adding the button layout to the main layout layout = QVBoxLayout() layout.addLayout(buttonLayout) layout.addWidget(self.imageLabel) + # Set the button editor to that script self.buttonEditor = ButtonEditor(self) + # Defining the overall layout 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() + 60) - self.setMouseTracking(True) - + # Let the app track the users mouse + self.setMouseTracking(True) self.lastClickPos = QPoint() - self.coordinates = np.empty((0, 2), dtype=int) - self.lastClickTime = 0 - self.nodeSize = 35 - self.handleSize = 15 - self.rotationHandleDistance = 35 - self.controlPoints = [] - self.rotationHandles = [] - self.nodeAngles = [] - self.draggingControlPoint = False + # Variables + self.coordinates = np.empty((0, 2), dtype=int) # Define the coordinate array again + self.lastClickTime = 0 # The last time the user clicked on the sceen + self.nodeSize = 35 # How large to make the nodes + self.handleSize = 15 # How large to make the rotation/control handles + self.rotationHandleDistance = 35 # How far the rotation handles are from the node + self.controlPoints = [] # Stores all of the control points + self.rotationHandles = [] # Stores all of the rotation handles + self.nodeAngles = [] # Stores all of the node angles + + # Tell what the user is currently dragging + self.draggingControlPoint = False self.draggingNode = False self.draggingRotationHandle = False self.draggingControlPointIndex = (-1, -1) self.draggingNodeIndex = -1 self.draggingRotationHandleIndex = -1 + # Tell when the user presses a key down def keyPressEvent(self, event: QKeyEvent): if event.key() == Qt.Key_R: + # Clear the auto self.showClearWarning() + + + # Tell when the user presses a mouse button def mousePressEvent(self, event: QMouseEvent): + # Get the position of the mouse click pos = self.imageLabel.mapFrom(self, event.position().toPoint()) x, y = pos.x(), pos.y() + # Tell if the mouse was click ed in the window if 0 <= x < self.pixmap.width() and 0 <= y < self.pixmap.height(): + # If right clicked, add a node if event.button() == Qt.RightButton: self.coordinates = np.vstack((self.coordinates, [x, y])) self.nodeAngles.append(0) @@ -321,7 +347,11 @@ class PathPlanner(QMainWindow): def showButtonEditor(self): self.hide() - self.buttonEditor.show() + self.buttonEditor.show() + + def showAbout(self): + self.about_window = AboutWindow() + self.about_window.show() if __name__ == "__main__": app = QApplication(sys.argv)