Example #1
0
    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
Example #2
0
    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)
Example #3
0
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()
Example #4
0
    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()
Example #5
0
    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)
Example #6
0
    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)
Example #7
0
    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))))
Example #8
0
    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 = ""
Example #9
0
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
Example #10
0
    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)
Example #11
0
 def paint(self, painter: QPainter, rect, mode, state):
     """Paint the icon int ``rect`` using ``painter``."""
     renderer = QSvgRenderer(self.data)
     renderer.render(painter, QRectF(rect))
Example #12
0
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
Example #13
0
 def __init__(self):
     self.renderer = QSvgRenderer()
Example #14
0
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())
Example #15
0
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())