source: orange/Orange/OrangeCanvas/gui/lineedit.py @ 11452:ce769ae3b71e

Revision 11452:ce769ae3b71e, 6.3 KB checked in by Ales Erjavec <ales.erjavec@…>, 12 months ago (diff)

Fixed LineEdit's initial size/layout.

RevLine 
[11105]1"""
2A LineEdit class with a button on left/right side.
3"""
4from collections import namedtuple
5
6from PyQt4.QtGui import (
7    QLineEdit, QToolButton, QStyleOptionToolButton, QStylePainter,
8    QStyle, QPalette, QFontMetrics, QAction
9)
10
11from PyQt4.QtCore import Qt, QSize, QRect, qVersion
12from PyQt4.QtCore import pyqtSignal as Signal, pyqtProperty as Property
13
14
15_ActionSlot = \
16    namedtuple(
17        "_AcitonSlot",
18        ["position",  # Left/Right position
19         "action",    # QAction
20         "button",    # LineEditButton instance
21         "autoHide"]  # Auto hide when line edit is empty.
22        )
23
24
25class LineEditButton(QToolButton):
[11366]26    """
27    A button in the :class:`LineEdit`.
28    """
[11105]29    def __init__(self, parent=None, flat=True, **kwargs):
30        QToolButton.__init__(self, parent, **kwargs)
31
32        self.__flat = flat
33
34    def setFlat(self, flat):
35        if self.__flat != flat:
36            self.__flat = flat
37            self.update()
38
39    def flat(self):
40        return self.__flat
41
42    flat_ = Property(bool, fget=flat, fset=setFlat,
43                     designable=True)
44
45    def paintEvent(self, event):
46        if self.__flat:
47            opt = QStyleOptionToolButton()
48            self.initStyleOption(opt)
49            p = QStylePainter(self)
50            p.drawControl(QStyle.CE_ToolButtonLabel, opt)
51        else:
52            QToolButton.paintEvent(self, event)
53
54
55class LineEdit(QLineEdit):
[11366]56    """
57    A line edit widget with support for adding actions (buttons) to
[11105]58    the left/right of the edited text
59
60    """
[11366]61    #: Position flags
[11105]62    LeftPosition, RightPosition = 1, 2
63
[11366]64    #: Emitted when the action is triggered.
[11105]65    triggered = Signal(QAction)
[11366]66
67    #: The left action was triggered.
[11105]68    leftTriggered = Signal()
[11366]69
70    #: The right action was triggered.
[11105]71    rightTriggered = Signal()
72
73    def __init__(self, parent=None, **kwargs):
74        QLineEdit.__init__(self, parent, **kwargs)
75        self.__actions = [None, None]
76
77    def setAction(self, action, position=LeftPosition):
[11366]78        """
79        Set `action` to be displayed at `position`. Existing action
80        (if present) will be removed.
81
82        Parameters
83        ----------
84        action : :class:`QAction`
85        position : int
86            Position where to set the action (default: ``LeftPosition``).
[11105]87
88        """
89
90        curr = self.actionAt(position)
91        if curr is not None:
92            self.removeAction(position)
93
94        # Add the action using QWidget.addAction (for shortcuts)
95        QLineEdit.addAction(self, action)
96
97        button = LineEditButton(self)
98        button.setToolButtonStyle(Qt.ToolButtonIconOnly)
99        button.setDefaultAction(action)
100        button.setVisible(self.isVisible())
101        button.show()
102        button.setCursor(Qt.ArrowCursor)
103
104        button.triggered.connect(self.triggered)
105        button.triggered.connect(self.__onTriggered)
106
107        slot = _ActionSlot(position, action, button, False)
108        self.__actions[position - 1] = slot
[11452]109
110        if not self.testAttribute(Qt.WA_Resized):
111            # Need some sensible height to do the layout.
112            self.adjustSize()
113
[11105]114        self.__layoutActions()
115
116    def actionAt(self, position):
[11366]117        """
118        Return :class:`QAction` at `position`.
[11105]119        """
120        self._checkPosition(position)
121        slot = self.__actions[position - 1]
122        if slot:
123            return slot.action
124        else:
125            return None
126
127    def removeActionAt(self, position):
[11366]128        """
129        Remove the action at position.
[11105]130        """
131        self._checkPosition(position)
132
133        slot = self.__actions[position - 1]
134        self.__actions[position - 1] = None
135
136        slot.button.hide()
137        slot.button.deleteLater()
138        QLineEdit.removeAction(self, slot.action)
139        self.__layoutActions()
140
141    def button(self, position):
[11366]142        """
143        Return the button (:class:`LineEditButton`) for the action
144        at `position`.
145
[11105]146        """
147        self._checkPosition(position)
148        slot = self.__actions[position - 1]
149        if slot:
150            return slot.button
151        else:
152            return None
153
154    def _checkPosition(self, position):
155        if position not in [self.LeftPosition, self.RightPosition]:
156            raise ValueError("Invalid position")
157
158    def resizeEvent(self, event):
159        QLineEdit.resizeEvent(self, event)
160        self.__layoutActions()
161
162    if qVersion() < "4.7":
163        # Qt 4.6 does not yet have placeholder text
164        def setPlaceholderText(self, text):
165            self.__placeholderText = text
166            self.update()
167
168        def placeholderText(self):
169            try:
170                return self.__placeholderText
171            except AttributeError:
172                return ""
173
174        def paintEvent(self, event):
175            QLineEdit.paintEvent(self, event)
176            if not self.text() and self.placeholderText() and \
177                    not self.hasFocus():
178                p = QStylePainter(self)
179                font = self.font()
180                metrics = QFontMetrics(font)
181                p.setFont(font)
182                color = self.palette().color(QPalette.Mid)
183                p.setPen(color)
184                left, top, right, bottom = self.getTextMargins()
185                contents = self.contentsRect()
186                contents = contents.adjusted(left, top, -right, -bottom)
187                text = metrics.elidedText(self.placeholderText(),
188                                          Qt.ElideMiddle,
189                                          contents.width())
190                p.drawText(contents, Qt.AlignLeft | Qt.AlignVCenter, text)
191
192    def __layoutActions(self):
193        left, right = self.__actions
194
195        contents = self.contentsRect()
196        buttonSize = QSize(contents.height(), contents.height())
197
198        margins = self.textMargins()
199
200        if left:
201            geom = QRect(contents.topLeft(), buttonSize)
202            left.button.setGeometry(geom)
203            margins.setLeft(buttonSize.width())
204
205        if right:
206            geom = QRect(contents.topRight(), buttonSize)
207            right.button.setGeometry(geom.translated(-buttonSize.width(), 0))
208            margins.setLeft(buttonSize.width())
209
210        self.setTextMargins(margins)
211
212    def __onTriggered(self, action):
213        left, right = self.__actions
214        if left and action == left.action:
215            self.leftTriggered.emit()
216        elif right and action == right.action:
217            self.rightTriggered.emit()
Note: See TracBrowser for help on using the repository browser.