def check_image(self, filename): """ Checks a filename to see if the image can be loaded. Parameters ---------- filename : (str) Inputted filename by user Returns ------- tuple : (str, misc) Error message and None if an error is present or None and a QSvgRenderer/QPixmap (depending on file type). """ error = None file_type = None abs_path = os.path.expanduser(os.path.expandvars(filename)) if not os.path.isabs(abs_path): try: if is_qt_designer(): p = self.get_designer_window() if p is not None: ui_dir = p.absoluteDir().absolutePath() abs_path = os.path.join(ui_dir, abs_path) else: parent_display = self.widget.find_parent_display() base_path = None if parent_display: base_path = os.path.dirname( parent_display.loaded_file()) abs_path = find_file(abs_path, base_path=base_path) except Exception as ex: print("Exception: ", ex) error = "Unable to find full filepath for {}".format(filename) abs_path = filename # First, lets try SVG. We have to try SVG first, otherwise # QPixmap will happily load the SVG and turn it into a raster image. # Really annoying: We have to try to load the file as SVG, # and we expect it will fail often (because many images aren't SVG). # Qt prints a warning message to stdout any time SVG loading fails. # So we have to temporarily silence Qt warning messages here. qInstallMessageHandler(self.qt_message_handler) svg = QSvgRenderer() if svg.load(abs_path): file_type = svg qInstallMessageHandler(None) return error, file_type qInstallMessageHandler(None) # SVG didn't work, lets try QPixmap image = QPixmap(abs_path) if not image.isNull(): file_type = image return error, file_type # If we get this far, the file specified could not be loaded at all. if error is None: error = "Could not load image \n{}".format(filename) return error, file_type
def __init__(self, lon0, lat0, lon1, lat1, svg_filename, parent=None): """Constructor. Args: longitude(float): Longitude of the upper left corner latitude(float): Latitude of the upper left corner longitude(float): Longitude of the lower right corner latitude(float): Latitude of the lower right corner svg_filename: Svg file name scene(MapGraphicsScene): Scene the item belongs to. parent(QGraphicsItem): Parent item. This will display an svg file with the corners geo-registered """ QGraphicsSvgItem.__init__(self, svg_filename, parent=parent) MapItem.__init__(self) self._lon0 = lon0 self._lat0 = lat0 self._lon1 = lon1 self._lat1 = lat1 self._xsize = 0 self._ysize = 0 self.x_mult = 1 self.y_mult = 1 self._renderer = QSvgRenderer(svg_filename) self._border = QGraphicsRectItem(parent=self) self._border.setPen(Qt.black)
class SvgView(QGraphicsView): Native, OpenGL, Image = range(3) def __init__(self, parent=None): super(SvgView, self).__init__(parent) self.signal = NodeSignal() self.renderer = SvgView.Native self.__svg_items = [] self.__wrapper_item = None self.__svg_renderer = QSvgRenderer() self.setScene(QGraphicsScene(self)) self.setTransformationAnchor(QGraphicsView.AnchorUnderMouse) self.setDragMode(QGraphicsView.ScrollHandDrag) self.setViewportUpdateMode(QGraphicsView.FullViewportUpdate) self.setViewport(QWidget()) self.setBackgroundBrush( QBrush(QColor(QPalette().color(QPalette.Active, QPalette.Window)))) def render_bytes(self, svg_bytes: bytes): s = self.scene() s.clear() self.__svg_items = [] self.resetTransform() self.__svg_renderer.load(QByteArray(svg_bytes)) import xml.etree.ElementTree as ET g = "{http://www.w3.org/2000/svg}g" xml = svg_bytes.decode('utf-8') # logger.debug(xml) for i in ET.fromstring(xml).findall(f"./{g}/{g}"): if i.attrib['class'] == 'edge': item = SvgItem(i.attrib['id'], self.__svg_renderer) else: node_name = next( (c.text for c in i if c.tag == '{http://www.w3.org/2000/svg}title'), None) if node_name: # logger.debug(f"Adding clickable item for {node_name}") item = ClickableSvgItem(i.attrib['id'], self.__svg_renderer, self.signal, node_name) else: # logger.debug(f"Adding standard item for {i.attrib['id']}") item = SvgItem(i.attrib['id'], self.__svg_renderer) item.setFlags(QGraphicsItem.ItemClipsToShape) item.setCacheMode(QGraphicsItem.NoCache) item.setZValue(1) s.addItem(item) rect: QRectF = s.itemsBoundingRect() rect.adjust(-10, -10, 10, 10) s.setSceneRect(rect) def wheelEvent(self, event): factor = pow(1.2, event.angleDelta().y() / 240.0) self.scale(factor, factor) event.accept()
def __init__(self, parent=None, **kwargs): """Class constructor.""" super().__init__(parent, **kwargs) self.m_state = 0 self.m_stateColors = [self.Red, self.Green] self.m_dsblColor = self.Gray self.m_shape = self.ShapeMap.Circle self._pressed = False self._isselected = False self.renderer = QSvgRenderer()
def imageFiles(self, new_files): """ JSON-formatted dictionary keyed on states (integers), with filenames of the image file to display for the state. Parameters ---------- new_files : str """ self._state_images_string = str(new_files) try: new_file_dict = json.loads(self._state_images_string) except Exception: self._state_images = {} return self._sizeHint = QSize(0, 0) for (state, filename) in new_file_dict.items(): if is_pydm_app(): try: file_path = self.app.get_path(filename) except Exception as e: logger.exception("Couldn't get file with path %s", filename) file_path = filename else: file_path = filename # First, lets try SVG. We have to try SVG first, otherwise # QPixmap will happily load the SVG and turn it into a raster image. # Really annoying: We have to try to load the file as SVG, # and we expect it will fail often (because many images aren't SVG). # Qt prints a warning message to stdout any time SVG loading fails. # So we have to temporarily silence Qt warning messages here. qInstallMessageHandler(self.qt_message_handler) svg = QSvgRenderer() svg.repaintNeeded.connect(self.update) if svg.load(file_path): self._state_images[int(state)] = (filename, svg) self._sizeHint = self._sizeHint.expandedTo(svg.defaultSize()) qInstallMessageHandler(None) continue qInstallMessageHandler(None) # SVG didn't work, lets try QPixmap image = QPixmap(file_path) if not image.isNull(): self._state_images[int(state)] = (filename, image) self._sizeHint = self._sizeHint.expandedTo(image.size()) continue # If we get this far, the file specified could not be loaded at all. logger.error("Could not load image: {}".format(filename)) self._state_images[int(state)] = (filename, None)
def __init__(self, parent=None): super(SvgView, self).__init__(parent) self.signal = NodeSignal() self.renderer = SvgView.Native self.__svg_items = [] self.__wrapper_item = None self.__svg_renderer = QSvgRenderer() self.setScene(QGraphicsScene(self)) self.setTransformationAnchor(QGraphicsView.AnchorUnderMouse) self.setDragMode(QGraphicsView.ScrollHandDrag) self.setViewportUpdateMode(QGraphicsView.FullViewportUpdate) self.setViewport(QWidget()) self.setBackgroundBrush( QBrush(QColor(QPalette().color(QPalette.Active, QPalette.Window))))
def __init__(self, parent=None, init_channel=None, invert=False): """Initialize all internal states and properties.""" QFrame.__init__(self, parent) PyDMWritableWidget.__init__(self, init_channel=init_channel) self._off = 0 self._on = 1 self.invert = invert self._bit = -1 self._bit_val = 0 self.value = 0 self.clicked.connect(self.send_value) self.shape = 0 self.renderer = QSvgRenderer() self._show_confirm_dialog = False self._confirm_message = PyDMStateButton.DEFAULT_CONFIRM_MESSAGE self._password_protected = False self._password = "" self._protected_password = ""
def create_splash_screen(): """Create splash screen.""" if not running_under_pytest(): image = QImage(500, 400, QImage.Format_ARGB32_Premultiplied) image.fill(0) painter = QPainter(image) renderer = QSvgRenderer(get_image_path('splash')) renderer.render(painter) painter.end() pm = QPixmap.fromImage(image) pm = pm.copy(0, 0, 500, 400) splash = QSplashScreen(pm) splash_font = splash.font() splash_font.setPixelSize(14) splash.setFont(splash_font) else: splash = None return splash
def setSvgDocument(self, svgDocument): """ Set a SVG icon as symbol :param svgDocument: SVG icon .. seealso:: :py:meth:`setGraphic()`, :py:meth:`setPixmap()` .. note:: The `style()` is set to `QwtSymbol.SvgDocument` .. note:: `brush()` and `pen()` have no effect """ self.__data.style = QwtSymbol.SvgDocument if self.__data.svg.renderer is None: self.__data.svg.renderer = QSvgRenderer() self.__data.svg.renderer.load(svgDocument)
def paint(self, painter: QPainter, rect, mode, state): """Paint the icon int ``rect`` using ``painter``.""" renderer = QSvgRenderer(self.data) renderer.render(painter, QRectF(rect))
class PyDMStateButton(QFrame, PyDMWritableWidget): """ A StateButton with support for Channels and much more from PyDM. It consists on QPushButton with internal state. Parameters ---------- parent : QWidget The parent widget for the Label init_channel : str, optional The channel to be used by the widget. """ class buttonShapeMap: """Enum class of shapes of button.""" locals().update(**BUTTONSHAPE) Q_ENUMS(buttonShapeMap) # enumMap for buttonShapeMap locals().update(**BUTTONSHAPE) squaredbuttonstatesdict = dict() path = _os.path.abspath(_os.path.dirname(__file__)) f = QFile(_os.path.join(path, 'resources/but_shapes/squared_on.svg')) if f.open(QFile.ReadOnly): squaredbuttonstatesdict['On'] = str(f.readAll(), 'utf-8') f.close() f = QFile(_os.path.join(path, 'resources/but_shapes/squared_off.svg')) if f.open(QFile.ReadOnly): squaredbuttonstatesdict['Off'] = str(f.readAll(), 'utf-8') f.close() f = QFile(_os.path.join(path, 'resources/but_shapes/squared_disconn.svg')) if f.open(QFile.ReadOnly): squaredbuttonstatesdict['Disconnected'] = str(f.readAll(), 'utf-8') f.close() roundedbuttonstatesdict = dict() f = QFile(_os.path.join(path, 'resources/but_shapes/rounded_on.svg')) if f.open(QFile.ReadOnly): roundedbuttonstatesdict['On'] = str(f.readAll(), 'utf-8') f.close() f = QFile(_os.path.join(path, 'resources/but_shapes/rounded_off.svg')) if f.open(QFile.ReadOnly): roundedbuttonstatesdict['Off'] = str(f.readAll(), 'utf-8') f.close() f = QFile(_os.path.join(path, 'resources/but_shapes/rounded_disconn.svg')) if f.open(QFile.ReadOnly): roundedbuttonstatesdict['Disconnected'] = str(f.readAll(), 'utf-8') f.close() clicked = Signal() DEFAULT_CONFIRM_MESSAGE = "Are you sure you want to proceed?" def __init__(self, parent=None, init_channel=None, invert=False): """Initialize all internal states and properties.""" QFrame.__init__(self, parent) PyDMWritableWidget.__init__(self, init_channel=init_channel) self._off = 0 self._on = 1 self.invert = invert self._bit = -1 self._bit_val = 0 self.value = 0 self.clicked.connect(self.send_value) self.shape = 0 self.renderer = QSvgRenderer() self._show_confirm_dialog = False self._confirm_message = PyDMStateButton.DEFAULT_CONFIRM_MESSAGE self._password_protected = False self._password = "" self._protected_password = "" @Property(bool) def passwordProtected(self): """ Whether or not this button is password protected. Returns ------- bool """ return self._password_protected @passwordProtected.setter def passwordProtected(self, value): """ Whether or not this button is password protected. Parameters ---------- value : bool """ if self._password_protected != value: self._password_protected = value @Property(str) def password(self): """ Password to be encrypted using SHA256. .. warning:: To avoid issues exposing the password this method always returns an empty string. Returns ------- str """ return "" @password.setter def password(self, value): """ Password to be encrypted using SHA256. Parameters ---------- value : str The password to be encrypted """ if value is not None and value != "": sha = _hashlib.sha256() sha.update(value.encode()) # Use the setter as it also checks whether the existing password # is the same with the new one, and only updates if the new # password is different self.protectedPassword = sha.hexdigest() @Property(str) def protectedPassword(self): """ The encrypted password. Returns ------- str """ return self._protected_password @protectedPassword.setter def protectedPassword(self, value): if self._protected_password != value: self._protected_password = value @Property(bool) def showConfirmDialog(self): """ Wether or not to display a confirmation dialog. Returns ------- bool """ return self._show_confirm_dialog @showConfirmDialog.setter def showConfirmDialog(self, value): """ Wether or not to display a confirmation dialog. Parameters ---------- value : bool """ if self._show_confirm_dialog != value: self._show_confirm_dialog = value @Property(str) def confirmMessage(self): """ Message to be displayed at the Confirmation dialog. Returns ------- str """ return self._confirm_message @confirmMessage.setter def confirmMessage(self, value): """ Message to be displayed at the Confirmation dialog. Parameters ---------- value : str """ if self._confirm_message != value: self._confirm_message = value def mouseReleaseEvent(self, ev): """Deal with mouse clicks. Only accept clicks within the figure.""" cond = ev.button() == Qt.LeftButton cond &= ev.x() < self.width()/2+self.height() cond &= ev.x() > self.width()/2-self.height() if cond: self.clicked.emit() def value_changed(self, new_val): """ Callback invoked when the Channel value is changed. Display the value of new_val accordingly. If :attr:'pvBit' is n>=0 or positive the button display the state of the n-th digit of the channel. Parameters ---------- new_value : str, int, float, bool or np.ndarray The new value from the channel. The type depends on the channel. """ if isinstance(new_val, _np.ndarray): _log.warning('PyDMStateButton received a numpy array to ' + self.channel+' ('+str(new_val)+')!') return super(PyDMStateButton, self).value_changed(new_val) value = int(new_val) self.value = value if self._bit >= 0: value = (value >> self._bit) & 1 self._bit_val = value self.update() def confirm_dialog(self): """ Show the confirmation dialog with the proper message in case ```showConfirmMessage``` is True. Returns ------- bool True if the message was confirmed or if ```showCofirmMessage``` is False. """ if not self._show_confirm_dialog: return True if self._confirm_message == "": self._confirm_message = PyDMStateButton.DEFAULT_CONFIRM_MESSAGE msg = QMessageBox() msg.setIcon(QMessageBox.Question) msg.setText(self._confirm_message) msg.setStandardButtons(QMessageBox.Yes | QMessageBox.No) msg.setDefaultButton(QMessageBox.No) ret = msg.exec_() return not ret == QMessageBox.No def validate_password(self): """ If the widget is ```passwordProtected```, this method will propmt the user for the correct password. Returns ------- bool True in case the password was correct of if the widget is not password protected. """ if not self._password_protected: return True pwd, ok = QInputDialog().getText( None, "Authentication", "Please enter your password:"******"") pwd = str(pwd) if not ok or pwd == "": return False sha = _hashlib.sha256() sha.update(pwd.encode()) pwd_encrypted = sha.hexdigest() if pwd_encrypted != self._protected_password: msg = QMessageBox() msg.setIcon(QMessageBox.Critical) msg.setText("Invalid password.") msg.setWindowTitle("Error") msg.setStandardButtons(QMessageBox.Ok) msg.setDefaultButton(QMessageBox.Ok) msg.setEscapeButton(QMessageBox.Ok) msg.exec_() return False return True def send_value(self): """ Emit a :attr:`send_value_signal` to update channel value. If :attr:'pvBit' is n>=0 or positive the button toggles the state of the n-th digit of the channel. Otherwise it toggles the whole value. """ if not self._connected: return None if not self.confirm_dialog(): return None if not self.validate_password(): return None checked = not self._bit_val val = checked if self._bit >= 0: val = int(self.value) val ^= (-checked ^ val) & (1 << self._bit) # For explanation look: # https://stackoverflow.com/questions/47981/how-do-you-set-clear-and-toggle-a-single-bit self.send_value_signal[self.channeltype].emit(self.channeltype(val)) def sizeHint(self): """Return size hint to define size on initialization.""" return QSize(72, 36) def paintEvent(self, event): """Treat appearence changes based on connection state and value.""" self.style().unpolish(self) self.style().polish(self) if not self.isEnabled(): state = 'Disconnected' elif self._bit_val == self._on: state = 'On' elif self._bit_val == self._off: state = 'Off' else: state = 'Disconnected' if self.shape == 0: shape_dict = PyDMStateButton.squaredbuttonstatesdict elif self.shape == 1: shape_dict = PyDMStateButton.roundedbuttonstatesdict option = QStyleOption() option.initFrom(self) h = option.rect.height() w = option.rect.width() aspect = 2.0 ah = w/aspect aw = w if ah > h: ah = h aw = h*aspect x = abs(aw-w)/2.0 y = abs(ah-h)/2.0 bounds = QRectF(x, y, aw, ah) painter = QPainter(self) painter.setRenderHint(QPainter.Antialiasing, True) shape_str = shape_dict[state] buttonstate_bytearray = bytes(shape_str, 'utf-8') self.renderer.load(QByteArray(buttonstate_bytearray)) self.renderer.render(painter, bounds) @Property(buttonShapeMap) def shape(self): """ Property to define the shape of the button. Returns ------- int """ return self._shape @shape.setter def shape(self, new_shape): """ Property to define the shape of the button. Parameters ---------- value : int """ if new_shape in [PyDMStateButton.Rounded, PyDMStateButton.Squared]: self._shape = new_shape self.update() else: raise ValueError('Button shape not defined!') @Property(int) def pvbit(self): """ Property to define which PV bit to control. Returns ------- int """ return int(self._bit) @pvbit.setter def pvbit(self, bit): """ Property to define which PV bit to control. Parameters ---------- value : int """ if bit >= 0: self._bit = int(bit) @Property(bool) def invert(self): """ Property that indicates whether to invert button on/off representation. Return ------ bool """ return self._invert @invert.setter def invert(self, value): """ Property that indicates whether to invert button on/off representation. Parameters ---------- value: bool """ self._invert = value if self._invert: self._on = 0 self._off = 1 else: self._on = 1 self._off = 0
def __init__(self): self.renderer = QSvgRenderer()
class MapGraphicsGeoSvgItem(QGraphicsSvgItem, MapItem): QtParentClass = QGraphicsSvgItem def __init__(self, lon0, lat0, lon1, lat1, svg_filename, parent=None): """Constructor. Args: longitude(float): Longitude of the upper left corner latitude(float): Latitude of the upper left corner longitude(float): Longitude of the lower right corner latitude(float): Latitude of the lower right corner svg_filename: Svg file name scene(MapGraphicsScene): Scene the item belongs to. parent(QGraphicsItem): Parent item. This will display an svg file with the corners geo-registered """ QGraphicsSvgItem.__init__(self, svg_filename, parent=parent) MapItem.__init__(self) self._lon0 = lon0 self._lat0 = lat0 self._lon1 = lon1 self._lat1 = lat1 self._xsize = 0 self._ysize = 0 self.x_mult = 1 self.y_mult = 1 self._renderer = QSvgRenderer(svg_filename) self._border = QGraphicsRectItem(parent=self) self._border.setPen(Qt.black) def updatePosition(self, scene): pos0 = scene.posFromLonLat(self._lon0, self._lat0) pos1 = scene.posFromLonLat(self._lon1, self._lat1) self.prepareGeometryChange() xsize = abs(int(pos1[0] - pos0[0])) ysize = abs(int(pos0[1] - pos1[1])) rect = scene.sceneRect() x = rect.x() y = rect.y() width = rect.width() height = rect.height() self.ul_x = min(pos0[0], pos1[0]) self.ul_y = min(pos0[1], pos1[1]) self.lr_x = max(pos0[0], pos1[0]) self.lr_y = max(pos0[1], pos1[1]) #self.scale(width, height) #print ("screen rect: {0}:{1}, {2}:{3}".format(int(x), int(x+width), int(y), int(y+height)), # "img rect: {0}:{1}, {2}:{3}".format(int(self.ul_x), int(self.lr_x), int(self.ul_y), int(self.lr_y))) #if xsize != self._xsize or ysize != self._ysize: self._xsize = xsize self._ysize = ysize self.ul_x = min(pos0[0], pos1[0]) self.ul_y = min(pos0[1], pos1[1]) self.setPos(self.ul_x, self.ul_y) # Scaled approach - does weird smoothing def paint(self, painter, option, widget=None): #print (self.x_mult, self.y_mult, self.orig_pixmap.width(), self.orig_pixmap.height()) self._renderer.render(painter, QRectF(0, 0, self._xsize, self._ysize)) def boundingRect(self): return QRectF(0, 0, self._xsize, self._ysize) def getGeoRect(self): ''' get geo referenced rectangle for this object Returns: QRectF (upper left x, upper left y, width, height) ''' pos0 = self.scene().posFromLonLat(self._lon0, self._lat0) pos1 = self.scene().posFromLonLat(self._lon1, self._lat1) xsize = abs(int(pos1[0] - pos0[0])) ysize = abs(int(pos0[1] - pos1[1])) ul_x = min(pos0[0], pos1[0]) ul_y = min(pos0[1], pos1[1]) rect = QRectF(ul_x, ul_y, xsize, ysize) return rect def setLonLat(self, lon0, lat0, lon1, lat1): self._lon0 = lon0 self._lat0 = lat0 self._lon1 = lon1 self._lat1 = lat1 scene = self.scene() if scene is not None: self.updatePosition(self.scene())
class QLed(QFrame, ShapeMap): """QLed class.""" ShapeMap = ShapeMap Q_ENUMS(ShapeMap) abspath = _os.path.abspath(_os.path.dirname(__file__)) shapesdict = dict() f = QFile(_os.path.join(abspath, 'resources/led_shapes/circle.svg')) if f.open(QFile.ReadOnly): shapesdict[ShapeMap.Circle] = str(f.readAll(), 'utf-8') f.close() f = QFile(_os.path.join(abspath, 'resources/led_shapes/round.svg')) if f.open(QFile.ReadOnly): shapesdict[ShapeMap.Round] = str(f.readAll(), 'utf-8') f.close() f = QFile(_os.path.join(abspath, 'resources/led_shapes/square.svg')) if f.open(QFile.ReadOnly): shapesdict[ShapeMap.Square] = str(f.readAll(), 'utf-8') f.close() f = QFile(_os.path.join(abspath, 'resources/led_shapes/triangle.svg')) if f.open(QFile.ReadOnly): shapesdict[ShapeMap.Triangle] = str(f.readAll(), 'utf-8') f.close() Green = QColor(15, 105, 0) Red = QColor(207, 0, 0) Gray = QColor(90, 90, 90) SelColor = QColor(0, 0, 0) NotSelColor1 = QColor(251, 244, 252) NotSelColor2 = QColor(173, 173, 173) clicked = Signal() selected = Signal(bool) def __init__(self, parent=None, **kwargs): """Class constructor.""" super().__init__(parent, **kwargs) self.m_state = 0 self.m_stateColors = [self.Red, self.Green] self.m_dsblColor = self.Gray self.m_shape = self.ShapeMap.Circle self._pressed = False self._isselected = False self.renderer = QSvgRenderer() def getState(self): """Value property getter.""" return self.m_state def setState(self, value): """Value property setter.""" self.m_state = value self.update() state = Property(bool, getState, setState) def getOnColor(self): """On color property getter.""" return self.m_stateColors[1] def setOnColor(self, newColor): """On color property setter.""" self.m_stateColors[1] = newColor self.update() onColor = Property(QColor, getOnColor, setOnColor) def getOffColor(self): """Off color property getter.""" return self.m_stateColors[0] def setOffColor(self, newColor): """Off color property setter.""" self.m_stateColors[0] = newColor self.update() offColor = Property(QColor, getOffColor, setOffColor) @property def stateColors(self): """Color list property getter.""" return list(self.m_stateColors) @stateColors.setter def stateColors(self, new_colors): """Color list property setter.""" if not isinstance(new_colors, (list, tuple)) or\ len(new_colors) < 2 or not isinstance(new_colors[0], QColor): return self.m_stateColors = list(new_colors) def getDsblColor(self): """Disabled color property getter.""" return self.m_dsblColor def setDsblColor(self, newColor): """Disabled color property setter.""" self.m_dsblColor = newColor self.update() dsblColor = Property(QColor, getDsblColor, setDsblColor) def getShape(self): """Shape property getter.""" return self.m_shape def setShape(self, newShape): """Shape property setter.""" self.m_shape = newShape self.update() shape = Property(ShapeMap, getShape, setShape) def sizeHint(self): """Return the base size of the widget according to shape.""" if self.m_shape == self.ShapeMap.Triangle: return QSize(48, 36) elif self.m_shape == self.ShapeMap.Round: return QSize(72, 36) return QSize(36, 36) def adjust(self, r, g, b): """Adjust the color to set on svg code.""" def normalise(x): return x / 255.0 def denormalise(x): if x <= 1: return int(x * 255.0) else: return 255.0 (h, l, s) = rgb_to_hls(normalise(r), normalise(g), normalise(b)) (nr, ng, nb) = hls_to_rgb(h, l * 1.5, s) return (denormalise(nr), denormalise(ng), denormalise(nb)) def getRGBfromQColor(self, qcolor): """Convert QColors to a tupple of rgb colors to set on svg code.""" redhex = qcolor.red() greenhex = qcolor.green() bluehex = qcolor.blue() return (redhex, greenhex, bluehex) def paintEvent(self, event): """Handle appearence of the widget on state updates.""" self.style().unpolish(self) self.style().polish(self) option = QStyleOption() option.initFrom(self) h = option.rect.height() w = option.rect.width() if self.m_shape in (self.ShapeMap.Triangle, self.ShapeMap.Round): aspect = (4 / 3.0) if self.m_shape == self.ShapeMap.Triangle else 2.0 ah = w / aspect aw = w if ah > h: ah = h aw = h * aspect x = abs(aw - w) / 2.0 y = abs(ah - h) / 2.0 bounds = QRectF(x, y, aw, ah) else: size = min(w, h) x = abs(size - w) / 2.0 y = abs(size - h) / 2.0 bounds = QRectF(x, y, size, size) painter = QPainter(self) painter.setRenderHint(QPainter.Antialiasing, True) ind = self.m_state % len(self.m_stateColors) dark_r, dark_g, dark_b = self.getRGBfromQColor(self.m_stateColors[ind]) if not self.isEnabled(): dark_r, dark_g, dark_b = self.getRGBfromQColor(self.m_dsblColor) sel1_r, sel1_g, sel1_b = self.getRGBfromQColor(self.SelColor) sel2_r, sel2_g, sel2_b = self.getRGBfromQColor(self.SelColor) opc = '1.000' if not self.isSelected(): sel1_r, sel1_g, sel1_b = self.getRGBfromQColor(self.NotSelColor1) sel2_r, sel2_g, sel2_b = self.getRGBfromQColor(self.NotSelColor2) opc = '0.145' dark_str = "rgb(%d,%d,%d)" % (dark_r, dark_g, dark_b) light_str = "rgb(%d,%d,%d)" % self.adjust(dark_r, dark_g, dark_b) sel1_str = "rgb(%d,%d,%d)" % (sel1_r, sel1_g, sel1_b) sel2_str = "rgb(%d,%d,%d)" % (sel2_r, sel2_g, sel2_b) shape_bytes = bytes( self.shapesdict[self.m_shape] % (sel1_str, opc, sel2_str, dark_str, light_str), 'utf-8') self.renderer.load(QByteArray(shape_bytes)) self.renderer.render(painter, bounds) def mousePressEvent(self, event): """Handle mouse press event.""" self._pressed = True super().mousePressEvent(event) def mouseReleaseEvent(self, event): """Handle mouse release event.""" if self._pressed: self._pressed = False self.clicked.emit() super().mouseReleaseEvent(event) def toggleState(self): """Toggle state property.""" self.m_state = 0 if self.m_state else 1 self.update() def isSelected(self): """Return selected state of object.""" return self._isselected def setSelected(self, sel): """Configure selected state of object.""" self._isselected = bool(sel) self.selected.emit(self._isselected) self.update() def toggleSelected(self): """Toggle isSelected property.""" self.setSelected(not self.isSelected())