E5Gui/E5LineEdit.py

Fri, 09 Nov 2012 16:34:35 +0100

author
Detlev Offenbach <detlev@die-offenbachs.de>
date
Fri, 09 Nov 2012 16:34:35 +0100
changeset 2206
b2da5a8d7f15
parent 2084
d3f083dd0222
child 2293
ec6c1459f5b3
permissions
-rw-r--r--

Added a line edit widget with an internal clear button to E5LineEdit.py.

# -*- coding: utf-8 -*-

# Copyright (c) 2009 - 2012 Detlev Offenbach <detlev@die-offenbachs.de>
#

"""
Module implementing specialized line edits.
"""

from PyQt4.QtCore import pyqtSignal, Qt, QEvent, qVersion
from PyQt4.QtGui import QLineEdit, QStyle, QPainter, QPalette, QStyleOptionFrameV2, \
    QWidget, QHBoxLayout, QBoxLayout, QLayout, QApplication, QSpacerItem, QSizePolicy

from E5Gui.E5LineEditButton import E5LineEditButton

import UI.PixmapCache


class E5LineEditSideWidget(QWidget):
    """
    Class implementing the side widgets for the line edit class.
    """
    sizeHintChanged = pyqtSignal()
    
    def __init__(self, parent=None):
        """
        Constructor
        
        @param parent reference to the parent widget (QWidget)
        """
        super().__init__(parent)
    
    def event(self, evt):
        """
        Protected method to handle events.
        
        @param reference to the event (QEvent)
        @return flag indicating, whether the event was recognized (boolean)
        """
        if evt.type() == QEvent.LayoutRequest:
            self.sizeHintChanged.emit()
        return QWidget.event(self, evt)


class E5LineEdit(QLineEdit):
    """
    Class implementing a line edit widget showing some inactive text.
    """
    LeftSide = 0
    RightSide = 1
    
    def __init__(self, parent=None, inactiveText=""):
        """
        Constructor
        
        @param parent reference to the parent widget (QWidget)
        @param inactiveText text to be shown on inactivity (string)
        """
        super().__init__(parent)
        
        self.setMinimumHeight(22)
        
        if qVersion() < "4.7.0":
            self.__inactiveText = inactiveText
        else:
            self.setPlaceholderText(inactiveText)
        
        self.__leftWidget = E5LineEditSideWidget(self)
        self.__leftWidget.resize(0, 0)
        self.__leftLayout = QHBoxLayout(self.__leftWidget)
        self.__leftLayout.setContentsMargins(0, 0, 0, 0)
        if QApplication.isRightToLeft():
            self.__leftLayout.setDirection(QBoxLayout.RightToLeft)
        else:
            self.__leftLayout.setDirection(QBoxLayout.LeftToRight)
        self.__leftLayout.setSizeConstraint(QLayout.SetFixedSize)
        
        self.__rightWidget = E5LineEditSideWidget(self)
        self.__rightWidget.resize(0, 0)
        self.__rightLayout = QHBoxLayout(self.__rightWidget)
        self.__rightLayout.setContentsMargins(0, 0, 0, 0)
        if self.isRightToLeft():
            self.__rightLayout.setDirection(QBoxLayout.RightToLeft)
        else:
            self.__rightLayout.setDirection(QBoxLayout.LeftToRight)
        horizontalSpacer = QSpacerItem(0, 0, QSizePolicy.Expanding, QSizePolicy.Minimum)
        self.__rightLayout.addItem(horizontalSpacer)
        
        self.setWidgetSpacing(3)
        self.__leftWidget.sizeHintChanged.connect(self._updateTextMargins)
        self.__rightWidget.sizeHintChanged.connect(self._updateTextMargins)
    
    def event(self, evt):
        """
        Protected method to handle events.
        
        @param reference to the event (QEvent)
        @return flag indicating, whether the event was recognized (boolean)
        """
        if evt.type() == QEvent.LayoutDirectionChange:
            if self.isRightToLeft():
                self.__leftLayout.setDirection(QBoxLayout.RightToLeft)
                self.__rightLayout.setDirection(QBoxLayout.RightToLeft)
            else:
                self.__leftLayout.setDirection(QBoxLayout.LeftToRight)
                self.__rightLayout.setDirection(QBoxLayout.LeftToRight)
        return QLineEdit.event(self, evt)
    
    def resizeEvent(self, evt):
        """
        Protected method to handle resize events.
        
        @param evt reference to the resize event (QResizeEvent)
        """
        self.__updateSideWidgetLocations()
        super().resizeEvent(evt)
    
    def paintEvent(self, evt):
        """
        Protected method handling a paint event.
        
        @param evt reference to the paint event (QPaintEvent)
        """
        super().paintEvent(evt)
        
        if qVersion() < "4.7.0":
            if not self.text() and \
               self.__inactiveText and \
               not self.hasFocus():
                panel = QStyleOptionFrameV2()
                self.initStyleOption(panel)
                textRect = \
                    self.style().subElementRect(QStyle.SE_LineEditContents, panel, self)
                textRect.adjust(2, 0, 0, 0)
                left = self.textMargin(self.LeftSide)
                right = self.textMargin(self.RightSide)
                textRect.adjust(left, 0, -right, 0)
                painter = QPainter(self)
                painter.setPen(self.palette().brush(
                    QPalette.Disabled, QPalette.Text).color())
                painter.drawText(
                    textRect, Qt.AlignLeft | Qt.AlignVCenter, self.__inactiveText)
    
    def __updateSideWidgetLocations(self):
        """
        Private method to update the side widget locations.
        """
        opt = QStyleOptionFrameV2()
        self.initStyleOption(opt)
        textRect = \
            self.style().subElementRect(QStyle.SE_LineEditContents, opt, self)
        textRect.adjust(2, 0, 0, 0)
        
        left = self.textMargin(self.LeftSide)
        
        midHeight = textRect.center().y() + 1
        
        if self.__leftLayout.count() > 0:
            leftHeight = midHeight - self.__leftWidget.height() // 2
            leftWidth = self.__leftWidget.width()
            if leftWidth == 0:
                leftHeight = midHeight - self.__leftWidget.sizeHint().height() // 2
            self.__leftWidget.move(textRect.x(), leftHeight)
        
        textRect.setX(left)
        textRect.setY(midHeight - self.__rightWidget.sizeHint().height() // 2)
        textRect.setHeight(self.__rightWidget.sizeHint().height())
        self.__rightWidget.setGeometry(textRect)
    
    def _updateTextMargins(self):
        """
        Protected slot to update the text margins.
        """
        left = self.textMargin(self.LeftSide)
        right = self.textMargin(self.RightSide)
        self.setTextMargins(left, 0, right, 0)
        self.__updateSideWidgetLocations()
    
    def addWidget(self, widget, position):
        """
        Public method to add a widget to a side.
        
        @param widget reference to the widget to add (QWidget)
        @param position position to add to (E5LineEdit.LeftSide, E5LineEdit.RightSide)
        """
        if widget is None:
            return
        
        if self.isRightToLeft():
            if position == self.LeftSide:
                position = self.RightSide
            else:
                position = self.LeftSide
        if position == self.LeftSide:
            self.__leftLayout.addWidget(widget)
        else:
            self.__rightLayout.insertWidget(1, widget)
    
    def removeWidget(self, widget):
        """
        Public method to remove a widget from a side.
        
        @param widget reference to the widget to remove (QWidget)
        """
        if widget is None:
            return
        
        self.__leftLayout.removeWidget(widget)
        self.__rightLayout.removeWidget(widget)
        widget.hide()
    
    def widgetSpacing(self):
        """
        Public method to get the side widget spacing.
        
        @return side widget spacing (integer)
        """
        return self.__leftLayout.spacing()
    
    def setWidgetSpacing(self, spacing):
        """
        Public method to set the side widget spacing.
        
        @param spacing side widget spacing (integer)
        """
        self.__leftLayout.setSpacing(spacing)
        self.__rightLayout.setSpacing(spacing)
        self._updateTextMargins()
    
    def textMargin(self, position):
        """
        Public method to get the text margin for a side.
        
        @param position side to get margin for (E5LineEdit.LeftSide, E5LineEdit.RightSide)
        """
        spacing = self.__rightLayout.spacing()
        w = 0
        if position == self.LeftSide:
            w = self.__leftWidget.sizeHint().width()
        else:
            w = self.__rightWidget.sizeHint().width()
        if w == 0:
            return 0
        return w + spacing * 2
    
    def inactiveText(self):
        """
        Public method to get the inactive text.
        
        return inactive text (string)
        """
        if qVersion() < "4.7.0":
            return self.__inactiveText
        else:
            return self.placeholderText()
    
    def setInactiveText(self, inactiveText):
        """
        Public method to set the inactive text.
        
        @param inactiveText text to be shown on inactivity (string)
        """
        if qVersion() < "4.7.0":
            self.__inactiveText = inactiveText
            self.update()
        else:
            self.setPlaceholderText(inactiveText)


class E5ClearableLineEdit(E5LineEdit):
    """
    Class implementing a line edit widget showing some inactive text and a clear button,
    if it has some contents.
    """
    def __init__(self, parent=None, inactiveText="", side=E5LineEdit.RightSide):
        """
        Constructor
        
        @param parent reference to the parent widget (QWidget)
        @keyparam inactiveText text to be shown on inactivity (string)
        @keyparam side side the clear button should be shown at (E5LineEdit.RightSide,
            E5LineEdit.LeftSide)
        """
        assert side in [E5LineEdit.RightSide, E5LineEdit.LeftSide]
        
        super().__init__(parent, inactiveText)
        
        self.__clearButton = E5LineEditButton(self)
        self.__clearButton.setIcon(UI.PixmapCache.getIcon("clearLeft.png"))
        self.addWidget(self.__clearButton, side)
        self.__clearButton.setVisible(False)
        
        self.__clearButton.clicked[()].connect(self.clear)
        self.textChanged.connect(self.__textChanged)
    
    def __textChanged(self, txt):
        """
        Private slot to handle changes of the text.
        
        @param txt text (string)
        """
        self.__clearButton.setVisible(txt != "")

eric ide

mercurial