Wed, 28 Apr 2021 19:42:28 +0200
Modernized some code.
# -*- 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)