diff --git a/Results/log.html b/Results/log.html deleted file mode 100644 index e13c174..0000000 --- a/Results/log.html +++ /dev/null @@ -1,2199 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-

Opening Robot Framework log failed

- -
- - - -
- - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/Results/output.xml b/Results/output.xml deleted file mode 100644 index 5806c98..0000000 --- a/Results/output.xml +++ /dev/null @@ -1,69 +0,0 @@ - - - - - -${URL} -${BROWSER} -Opens a new browser instance to the optional ``url``. -Opening browser 'Chrome' to base url 'https://www.wikipedia.org/'. -Cannot capture screenshot because no browser is open. -WebDriverException: Message: 'chromedriver' executable needs to be in PATH. Please see https://chromedriver.chromium.org/home - - - - -Maximizes current browser window. - - - -${SEARCH_FIELD} -timeout=5s -Waits until the element ``locator`` is visible. - - - -${SEARCH_FIELD} -Robot Framework -Types the given ``text`` into the text field identified by ``locator``. - - - -${SEARCH_BUTTON} -Click the element identified by ``locator``. - - - -${RESULT_TITLE} -timeout=5s -Waits until the element ``locator`` is visible. - - - -${RESULT_TITLE} -Robot Framework -Verifies that element ``locator`` contains text ``expected``. - - - -Closes the current browser. - - -WebDriverException: Message: 'chromedriver' executable needs to be in PATH. Please see https://chromedriver.chromium.org/home - - - - - - -All Tests - - - - -01 Test One - - - - - diff --git a/Results/report.html b/Results/report.html deleted file mode 100644 index 362ae7d..0000000 --- a/Results/report.html +++ /dev/null @@ -1,2462 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-

Opening Robot Framework report failed

- -
- - - -
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/ui/help/help_widget.py b/ui/help/help_widget.py index 09d126f..02a1a08 100644 --- a/ui/help/help_widget.py +++ b/ui/help/help_widget.py @@ -1,10 +1,10 @@ import os from PyQt6.QtWidgets import (QWidget, QVBoxLayout, QHBoxLayout, QLabel, QPushButton, - QFrame, QGraphicsOpacityEffect) + QFrame, QGraphicsOpacityEffect, QSizePolicy) from PyQt6.QtCore import (Qt, QSize, QRect, QPropertyAnimation, QEasingCurve, - QPointF, QPoint) + QPoint, QPointF) from PyQt6.QtGui import (QIcon, QPainter, QColor, QBrush, QPen, QPaintEvent, - QLinearGradient, QFont) + QLinearGradient, QFont, QRadialGradient) from utils.resource_utils import resource_path import webbrowser import sys @@ -14,27 +14,27 @@ class AnimatedCircleButton(QPushButton): def __init__(self, icon_path, text="", parent=None): super().__init__(parent) - self.setFixedSize(110, 110) + self.setFixedSize(120, 120) self.setCursor(Qt.CursorShape.PointingHandCursor) - self.font = QFont("Segoe UI", 9, QFont.Weight.Medium) + self.font = QFont("Segoe UI", 10, QFont.Weight.DemiBold) self.text = text - self.icon = QIcon(resource_path(f"images/{icon_path}")) + self.icon_path = icon_path # Store the path instead of loading immediately self.icon_size = 60 self._radius = 45 self.hover_radius = 50 self.base_radius = 45 # Colors - self.base_color = QColor(69, 71, 90, 80) - self.hover_color = QColor(34, 208, 83, 150) - self.text_color = QColor(200, 200, 200) + self.base_color = QColor(69, 71, 90, 120) + self.hover_color = QColor(34, 208, 83, 180) + self.text_color = QColor(220, 220, 220) self.highlight_color = QColor(34, 208, 83) # Animation setup - self.animation = QPropertyAnimation(self, b"radius") - self.animation.setDuration(200) - self.animation.setEasingCurve(QEasingCurve.Type.OutQuad) + self.radius_animation = QPropertyAnimation(self, b"_radius") + self.radius_animation.setDuration(300) + self.radius_animation.setEasingCurve(QEasingCurve.Type.OutBack) self.setStyleSheet(""" QPushButton { @@ -63,59 +63,64 @@ def set_radius(self, radius): radius = property(get_radius, set_radius) def enterEvent(self, event): - self.animation.stop() - self.animation.setStartValue(self._radius) - self.animation.setEndValue(self.hover_radius) - self.animation.start() + self.radius_animation.stop() + self.radius_animation.setStartValue(self._radius) + self.radius_animation.setEndValue(self.hover_radius) + self.radius_animation.start() super().enterEvent(event) def leaveEvent(self, event): - self.animation.stop() - self.animation.setStartValue(self._radius) - self.animation.setEndValue(self.base_radius) - self.animation.start() + self.radius_animation.stop() + self.radius_animation.setStartValue(self._radius) + self.radius_animation.setEndValue(self.base_radius) + self.radius_animation.start() super().leaveEvent(event) def paintEvent(self, event: QPaintEvent): painter = QPainter(self) painter.setRenderHint(QPainter.RenderHint.Antialiasing) - center = self.rect().center() + center = QPointF(self.rect().center()) # Draw glow effect when hovered if self.underMouse(): - gradient = QLinearGradient( - QPointF(center.x() - 30, center.y() - 30), - QPointF(center.x() + 30, center.y() + 30) - ) - gradient.setColorAt(0, QColor(34, 208, 83, 50)) - gradient.setColorAt(1, QColor(34, 208, 83, 20)) + gradient = QRadialGradient(center.x(), center.y(), self.hover_radius + 15) + gradient.setColorAt(0, QColor(34, 208, 83, 80)) + gradient.setColorAt(1, QColor(34, 208, 83, 0)) painter.setPen(Qt.PenStyle.NoPen) painter.setBrush(QBrush(gradient)) - painter.drawEllipse(center, self.hover_radius + 10, self.hover_radius + 10) + painter.drawEllipse(center, self.hover_radius + 15, self.hover_radius + 15) # Draw the background circle - painter.setPen(QPen(self.highlight_color if self.underMouse() else Qt.GlobalColor.transparent, 1.5)) + border_width = 2.5 if self.underMouse() else 1.5 + painter.setPen(QPen(self.highlight_color if self.underMouse() else QColor(100, 100, 100, 100), border_width)) painter.setBrush(QBrush(self.hover_color if self.underMouse() else self.base_color)) painter.drawEllipse(center, self._radius, self._radius) + # Load icon fresh each time to prevent disappearing + icon = QIcon(resource_path(f"images/{self.icon_path}")) + # Draw the icon icon_rect = QRect( - center.x() - self.icon_size//2, - center.y() - self.icon_size//2, + int(center.x() - self.icon_size//2), + int(center.y() - self.icon_size//2), self.icon_size, self.icon_size ) - self.icon.paint(painter, icon_rect) + + # Always draw icon in normal mode + icon.paint(painter, icon_rect, Qt.AlignmentFlag.AlignCenter, + QIcon.Mode.Normal, QIcon.State.On) # Draw the text below the circle if provided if self.text: painter.setFont(self.font) - painter.setPen(QPen(self.text_color)) + text_color = self.highlight_color if self.underMouse() else self.text_color + painter.setPen(QPen(text_color)) text_rect = QRect( - center.x() - 50, - center.y() + self._radius + 5, + int(center.x() - 50), + int(center.y() + self._radius + 8), 100, 20 ) @@ -132,46 +137,56 @@ def init_ui(self): QWidget { background: qlineargradient( x1:0, y1:0, x2:1, y2:1, - stop:0 #1e1f2c, stop:1 #2a2b3a + stop:0 #1a1b26, stop:1 #24283b ); - border-radius: 12px; + border-radius: 16px; } """) layout = QVBoxLayout() - layout.setContentsMargins(30, 20, 30, 30) + layout.setContentsMargins(40, 30, 40, 30) layout.setSpacing(25) # Title with decorative elements title_container = QHBoxLayout() title_container.setContentsMargins(0, 0, 0, 10) + title_container.setSpacing(15) - left_decoration = QFrame() - left_decoration.setFrameShape(QFrame.Shape.HLine) - left_decoration.setStyleSheet("border: 1px solid #22d053;") - left_decoration.setFixedWidth(30) + # Create icon once and reuse + help_icon = QIcon(resource_path("images/help_icon.png")) + icon_pixmap = help_icon.pixmap(QSize(24, 24)) - right_decoration = QFrame() - right_decoration.setFrameShape(QFrame.Shape.HLine) - right_decoration.setStyleSheet("border: 1px solid #22d053;") - right_decoration.setFixedWidth(30) + left_decoration = QLabel() + left_decoration.setPixmap(icon_pixmap) - title = QLabel("Quick Links") + right_decoration = QLabel() + right_decoration.setPixmap(icon_pixmap) + + title = QLabel("Help & Resources") title.setAlignment(Qt.AlignmentFlag.AlignCenter) title.setStyleSheet(""" QLabel { - font: 20pt "Segoe UI SemiBold"; - color: #ffffff; + font: 24pt "Segoe UI SemiBold"; + color: #e0e0e0; padding: 0 15px; } """) title_container.addWidget(left_decoration, 0, Qt.AlignmentFlag.AlignVCenter) + title_container.addStretch(1) title_container.addWidget(title, 0, Qt.AlignmentFlag.AlignCenter) + title_container.addStretch(1) title_container.addWidget(right_decoration, 0, Qt.AlignmentFlag.AlignVCenter) layout.addLayout(title_container) + # Decorative line + line = QFrame() + line.setFrameShape(QFrame.Shape.HLine) + line.setStyleSheet("border: 1px solid #22d053; border-radius: 1px;") + line.setFixedHeight(2) + layout.addWidget(line) + description = QLabel( "Access important resources and support channels with one click. " "Get help, report issues, or contribute to the project." @@ -180,35 +195,38 @@ def init_ui(self): description.setWordWrap(True) description.setStyleSheet(""" QLabel { - font: 10pt "Segoe UI"; - color: #a0a0a0; - padding: 0 20px; + font: 11pt "Segoe UI"; + color: #b0b0b0; + padding: 10px 30px; } """) layout.addWidget(description) + # Button grid layout buttons_layout = QHBoxLayout() - buttons_layout.setContentsMargins(20, 0, 20, 0) - buttons_layout.setSpacing(30) + buttons_layout.setContentsMargins(20, 10, 20, 20) + buttons_layout.setSpacing(40) buttons = [ ("Report Issue", "bug.png", "https://github.com/ROBOT-RUNNER-COMMUNITY/ROBOT-RUNNER/issues"), ("GitHub", "github.png", "https://github.com/ROBOT-RUNNER-COMMUNITY/ROBOT-RUNNER"), - ("Docs", "docs.png", "https://robot-runner-doc.readthedocs.io/en/latest/"), + ("Documentation", "docs.png", "https://robot-runner-doc.readthedocs.io/en/latest/"), ("Support", "donate.png", "https://buymeacoffee.com/khabarachre") ] for text, icon, url in buttons: btn = AnimatedCircleButton(icon, text) + btn.setToolTip(f"Open {text} in browser") btn.clicked.connect(lambda _, u=url: webbrowser.open(u)) buttons_layout.addWidget(btn, 0, Qt.AlignmentFlag.AlignCenter) layout.addLayout(buttons_layout) + # Version information config_paths = [ - 'config.xml', - os.path.join(os.path.dirname(sys.executable), 'config.xml'), - ] + 'config.xml', + os.path.join(os.path.dirname(sys.executable), 'config.xml'), + ] config_loaded = False for path in config_paths: @@ -220,33 +238,50 @@ def init_ui(self): break if not config_loaded: - self.version_label = "(Unknown version)" + self.version_label = "(Unknown)" - footer = QLabel(f"© ROBOT RUNNER {self.version_label} | " - "Developed by Achraf KHABAR | ") - footer.setAlignment(Qt.AlignmentFlag.AlignCenter) - footer.setStyleSheet(""" + # Footer with version and copyright + footer = QHBoxLayout() + footer.setContentsMargins(0, 20, 0, 0) + + version_label = QLabel(f"{self.version_label}") + version_label.setAlignment(Qt.AlignmentFlag.AlignLeft) + version_label.setStyleSheet(""" + QLabel { + font: 9pt "Segoe UI"; + color: #606060; + } + """) + + copyright_label = QLabel("© ROBOT RUNNER | Developed by Achraf KHABAR") + copyright_label.setAlignment(Qt.AlignmentFlag.AlignCenter) + copyright_label.setStyleSheet(""" QLabel { - font: 8pt "Segoe UI"; + font: 9pt "Segoe UI"; color: #606060; - padding-top: 15px; - border-top: 1px solid #404040; } """) - layout.addWidget(footer) + + spacer = QWidget() + spacer.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Preferred) + + footer.addWidget(version_label) + footer.addWidget(copyright_label) + footer.addWidget(spacer) + + layout.addLayout(footer) self.setLayout(layout) - def setup_animations(self): self.opacity_effect = QGraphicsOpacityEffect(self) self.setGraphicsEffect(self.opacity_effect) self.fade_in = QPropertyAnimation(self.opacity_effect, b"opacity") - self.fade_in.setDuration(500) + self.fade_in.setDuration(600) self.fade_in.setStartValue(0) self.fade_in.setEndValue(1) - self.fade_in.setEasingCurve(QEasingCurve.Type.OutQuad) + self.fade_in.setEasingCurve(QEasingCurve.Type.OutBack) def showEvent(self, event): self.fade_in.start() @@ -256,18 +291,26 @@ def paintEvent(self, event): painter = QPainter(self) painter.setRenderHint(QPainter.RenderHint.Antialiasing) - # Shadow effect - shadow = QLinearGradient(0, 0, 0, self.height()) - shadow.setColorAt(0, QColor(0, 0, 0, 30)) - shadow.setColorAt(1, QColor(0, 0, 0, 60)) + # Main background with gradient + gradient = QLinearGradient(0, 0, self.width(), self.height()) + gradient.setColorAt(0, QColor(26, 27, 38)) + gradient.setColorAt(1, QColor(36, 40, 59)) painter.setPen(Qt.PenStyle.NoPen) - painter.setBrush(QBrush(shadow)) - painter.drawRoundedRect(self.rect().adjusted(0, 3, 0, 3), 12, 12) + painter.setBrush(QBrush(gradient)) + painter.drawRoundedRect(self.rect(), 16, 16) - # Main background - painter.setBrush(QBrush(QColor(30, 31, 46))) - painter.setPen(QPen(QColor(60, 62, 82), 1)) - painter.drawRoundedRect(self.rect(), 12, 12) + # Border glow effect + if self.underMouse(): + border_rect = self.rect().adjusted(1, 1, -1, -1) + border_gradient = QLinearGradient(0, 0, self.width(), self.height()) + border_gradient.setColorAt(0, QColor(34, 208, 83, 80)) + border_gradient.setColorAt(1, QColor(34, 208, 83, 30)) + + pen = QPen(QBrush(border_gradient), 2) + pen.setJoinStyle(Qt.PenJoinStyle.RoundJoin) + painter.setPen(pen) + painter.setBrush(Qt.BrushStyle.NoBrush) + painter.drawRoundedRect(border_rect, 16, 16) super().paintEvent(event) \ No newline at end of file