diff -r 4e8b98454baa -r 800c432b34c8 eric7/E5Gui/E5Led.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/eric7/E5Gui/E5Led.py Sat May 15 18:45:04 2021 +0200 @@ -0,0 +1,336 @@ +# -*- coding: utf-8 -*- + +# Copyright (c) 2006 - 2021 Detlev Offenbach <detlev@die-offenbachs.de> +# + +""" +Module implementing a LED widget. + +It was inspired by KLed. +""" + +import enum + +from PyQt5.QtCore import pyqtSignal, Qt, QSize, QPoint +from PyQt5.QtGui import QColor, QRadialGradient, QPalette, QPainter, QBrush +from PyQt5.QtWidgets import QWidget + + +class E5LedType(enum.Enum): + """ + Class defining the LED types. + """ + RECTANGULAR = 0 + CIRCULAR = 1 + + +class E5Led(QWidget): + """ + Class implementing a LED widget. + """ + def __init__(self, parent=None, color=None, shape=E5LedType.CIRCULAR, + rectRatio=1): + """ + Constructor + + @param parent reference to parent widget + @type QWidget + @param color color of the LED + @type QColor + @param shape shape of the LED + @type E5LedType + @param rectRatio ratio width to height, if shape is rectangular + @type float + """ + super().__init__(parent) + + if color is None: + color = QColor("green") + + self.__led_on = True + self.__dark_factor = 300 + self.__offcolor = color.darker(self.__dark_factor) + self.__led_color = color + self.__framedLed = True + self.__shape = shape + self.__rectRatio = rectRatio + + self.setColor(color) + + def paintEvent(self, evt): + """ + Protected slot handling the paint event. + + @param evt paint event object + @type QPaintEvent + """ + if self.__shape == E5LedType.CIRCULAR: + self.__paintRound() + elif self.__shape == E5LedType.RECTANGULAR: + self.__paintRectangular() + + def __getBestRoundSize(self): + """ + Private method to calculate the width of the LED. + + @return new width of the LED (integer) + """ + width = min(self.width(), self.height()) + width -= 2 # leave one pixel border + return width > -1 and width or 0 + + def __paintRound(self): + """ + Private method to paint a round raised LED. + """ + # Initialize coordinates, width and height of the LED + width = self.__getBestRoundSize() + + # Calculate the gradient for the LED + wh = width / 2 + color = self.__led_on and self.__led_color or self.__offcolor + gradient = QRadialGradient(wh, wh, wh, 0.8 * wh, 0.8 * wh) + gradient.setColorAt(0.0, color.lighter(200)) + gradient.setColorAt(0.6, color) + if self.__framedLed: + gradient.setColorAt(0.9, color.darker()) + gradient.setColorAt( + 1.0, self.palette().color(QPalette.ColorRole.Dark)) + else: + gradient.setColorAt(1.0, color.darker()) + + # now do the drawing + paint = QPainter(self) + paint.setRenderHint(QPainter.RenderHint.Antialiasing, True) + paint.setBrush(QBrush(gradient)) + paint.setPen(Qt.PenStyle.NoPen) + paint.drawEllipse(1, 1, width, width) + paint.end() + + def __paintRectangular(self): + """ + Private method to paint a rectangular raised LED. + """ + # Initialize coordinates, width and height of the LED + width = self.height() * self.__rectRatio + left = max(0, int((self.width() - width) / 2) - 1) + right = min(int((self.width() + width) / 2), self.width()) + height = self.height() + + # now do the drawing + painter = QPainter(self) + painter.setRenderHint(QPainter.RenderHint.Antialiasing, True) + color = self.__led_on and self.__led_color or self.__offcolor + + painter.setPen(color.lighter(200)) + painter.drawLine(left, 0, left, height - 1) + painter.drawLine(left + 1, 0, right - 1, 0) + if self.__framedLed: + painter.setPen(self.palette().color(QPalette.ColorRole.Dark)) + else: + painter.setPen(color.darker()) + painter.drawLine(left + 1, height - 1, right - 1, height - 1) + painter.drawLine(right - 1, 1, right - 1, height - 1) + painter.fillRect(left + 1, 1, right - 2, height - 2, QBrush(color)) + painter.end() + + def isOn(self): + """ + Public method to return the LED state. + + @return flag indicating the light state (boolean) + """ + return self.__led_on + + def shape(self): + """ + Public method to return the LED shape. + + @return LED shape + @rtype E5LedType + """ + return self.__shape + + def ratio(self): + """ + Public method to return the LED rectangular ratio [= width / height]. + + @return LED rectangular ratio (float) + """ + return self.__rectRatio + + def color(self): + """ + Public method to return the LED color. + + @return color of the LED (QColor) + """ + return self.__led_color + + def setOn(self, state): + """ + Public method to set the LED to on. + + @param state new state of the LED (boolean) + """ + if self.__led_on != state: + self.__led_on = state + self.update() + + def setShape(self, shape): + """ + Public method to set the LED shape. + + @param shape new LED shape + @type E5LedType + """ + if self.__shape != shape: + self.__shape = shape + self.update() + + def setRatio(self, ratio): + """ + Public method to set the LED rectangular ratio (width / height). + + @param ratio new LED rectangular ratio (float) + """ + if self.__rectRatio != ratio: + self.__rectRatio = ratio + self.update() + + def setColor(self, color): + """ + Public method to set the LED color. + + @param color color for the LED (QColor) + """ + if self.__led_color != color: + self.__led_color = color + self.__offcolor = color.darker(self.__dark_factor) + self.update() + + def setDarkFactor(self, darkfactor): + """ + Public method to set the dark factor. + + @param darkfactor value to set for the dark factor (integer) + """ + if self.__dark_factor != darkfactor: + self.__dark_factor = darkfactor + self.__offcolor = self.__led_color.darker(darkfactor) + self.update() + + def darkFactor(self): + """ + Public method to return the dark factor. + + @return the current dark factor (integer) + """ + return self.__dark_factor + + def toggle(self): + """ + Public slot to toggle the LED state. + """ + self.setOn(not self.__led_on) + + def on(self): + """ + Public slot to set the LED to on. + """ + self.setOn(True) + + def off(self): + """ + Public slot to set the LED to off. + """ + self.setOn(False) + + def setFramed(self, framed): + """ + Public slot to set the __framedLed attribute. + + @param framed flag indicating the framed state (boolean) + """ + if self.__framedLed != framed: + self.__framedLed = framed + self.__off_map = None + self.__on_map = None + self.update() + + def isFramed(self): + """ + Public method to return the framed state. + + @return flag indicating the current framed state (boolean) + """ + return self.__framedLed + + def sizeHint(self): + """ + Public method to give a hint about our desired size. + + @return size hint (QSize) + """ + return QSize(18, 18) + + def minimumSizeHint(self): + """ + Public method to give a hint about our minimum size. + + @return size hint (QSize) + """ + return QSize(18, 18) + + +class E5ClickableLed(E5Led): + """ + Class implementing a clickable LED widget. + + @signal clicked(QPoint) emitted upon a click on the LED with the + left button + @signal middleClicked(QPoint) emitted upon a click on the LED with + the middle button or CTRL and left button + """ + clicked = pyqtSignal(QPoint) + middleClicked = pyqtSignal(QPoint) + + def __init__(self, parent=None, color=None, shape=E5LedType.CIRCULAR, + rectRatio=1): + """ + Constructor + + @param parent reference to parent widget + @type QWidget + @param color color of the LED + @type QColor + @param shape shape of the LED + @type E5LedType + @param rectRatio ratio width to height, if shape is rectangular + @type float + """ + super().__init__(parent, color, shape, rectRatio) + + self.setCursor(Qt.CursorShape.PointingHandCursor) + + def mouseReleaseEvent(self, evt): + """ + Protected method handling mouse release events. + + @param evt mouse event (QMouseEvent) + """ + if ( + evt.button() == Qt.MouseButton.LeftButton and + self.rect().contains(evt.pos()) + ): + if evt.modifiers() == Qt.KeyboardModifier.ControlModifier: + self.middleClicked.emit(evt.globalPos()) + else: + self.clicked.emit(evt.globalPos()) + elif ( + evt.button() == Qt.MouseButton.MidButton and + self.rect().contains(evt.pos()) + ): + self.middleClicked.emit(evt.globalPos()) + else: + super().mouseReleaseEvent(evt)