source: orange/Orange/OrangeCanvas/gui/lineedit.py @ 11105:34a1ed8255dc

Revision 11105:34a1ed8255dc, 5.7 KB checked in by Ales Erjavec <ales.erjavec@…>, 19 months ago (diff)

Added a QLineEdit subclass with support for action/button in left/right side.

Line 
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):
26    def __init__(self, parent=None, flat=True, **kwargs):
27        QToolButton.__init__(self, parent, **kwargs)
28
29        self.__flat = flat
30
31    def setFlat(self, flat):
32        if self.__flat != flat:
33            self.__flat = flat
34            self.update()
35
36    def flat(self):
37        return self.__flat
38
39    flat_ = Property(bool, fget=flat, fset=setFlat,
40                     designable=True)
41
42    def paintEvent(self, event):
43        if self.__flat:
44            opt = QStyleOptionToolButton()
45            self.initStyleOption(opt)
46            p = QStylePainter(self)
47            p.drawControl(QStyle.CE_ToolButtonLabel, opt)
48        else:
49            QToolButton.paintEvent(self, event)
50
51
52class LineEdit(QLineEdit):
53    """A line edit widget with support for adding actions (buttons) to
54    the left/right of the edited text
55
56    """
57    LeftPosition, RightPosition = 1, 2
58
59    triggered = Signal(QAction)
60    leftTriggered = Signal()
61    rightTriggered = Signal()
62
63    def __init__(self, parent=None, **kwargs):
64        QLineEdit.__init__(self, parent, **kwargs)
65        self.__actions = [None, None]
66
67    def setAction(self, action, position=LeftPosition):
68        """Set `action` to be displayed at `position`. Existing action
69        if present will be removed.
70
71        """
72
73        curr = self.actionAt(position)
74        if curr is not None:
75            self.removeAction(position)
76
77        # Add the action using QWidget.addAction (for shortcuts)
78        QLineEdit.addAction(self, action)
79
80        button = LineEditButton(self)
81        button.setToolButtonStyle(Qt.ToolButtonIconOnly)
82        button.setDefaultAction(action)
83        button.setVisible(self.isVisible())
84        button.show()
85        button.setCursor(Qt.ArrowCursor)
86
87        button.triggered.connect(self.triggered)
88        button.triggered.connect(self.__onTriggered)
89
90        slot = _ActionSlot(position, action, button, False)
91        self.__actions[position - 1] = slot
92        self.__layoutActions()
93
94    def actionAt(self, position):
95        """Return `action` at `position`.
96        """
97        self._checkPosition(position)
98        slot = self.__actions[position - 1]
99        if slot:
100            return slot.action
101        else:
102            return None
103
104    def removeActionAt(self, position):
105        """Remove action at position.
106        """
107        self._checkPosition(position)
108
109        slot = self.__actions[position - 1]
110        self.__actions[position - 1] = None
111
112        slot.button.hide()
113        slot.button.deleteLater()
114        QLineEdit.removeAction(self, slot.action)
115        self.__layoutActions()
116
117    def button(self, position):
118        """Return the actions button at position.
119        """
120        self._checkPosition(position)
121        slot = self.__actions[position - 1]
122        if slot:
123            return slot.button
124        else:
125            return None
126
127    def _checkPosition(self, position):
128        if position not in [self.LeftPosition, self.RightPosition]:
129            raise ValueError("Invalid position")
130
131    def resizeEvent(self, event):
132        QLineEdit.resizeEvent(self, event)
133        self.__layoutActions()
134
135    if qVersion() < "4.7":
136        # Qt 4.6 does not yet have placeholder text
137        def setPlaceholderText(self, text):
138            self.__placeholderText = text
139            self.update()
140
141        def placeholderText(self):
142            try:
143                return self.__placeholderText
144            except AttributeError:
145                return ""
146
147        def paintEvent(self, event):
148            QLineEdit.paintEvent(self, event)
149            if not self.text() and self.placeholderText() and \
150                    not self.hasFocus():
151                p = QStylePainter(self)
152                font = self.font()
153                metrics = QFontMetrics(font)
154                p.setFont(font)
155                color = self.palette().color(QPalette.Mid)
156                p.setPen(color)
157                left, top, right, bottom = self.getTextMargins()
158                contents = self.contentsRect()
159                contents = contents.adjusted(left, top, -right, -bottom)
160                text = metrics.elidedText(self.placeholderText(),
161                                          Qt.ElideMiddle,
162                                          contents.width())
163                p.drawText(contents, Qt.AlignLeft | Qt.AlignVCenter, text)
164
165    def __layoutActions(self):
166        left, right = self.__actions
167
168        contents = self.contentsRect()
169        buttonSize = QSize(contents.height(), contents.height())
170
171        margins = self.textMargins()
172
173        if left:
174            geom = QRect(contents.topLeft(), buttonSize)
175            left.button.setGeometry(geom)
176            margins.setLeft(buttonSize.width())
177
178        if right:
179            geom = QRect(contents.topRight(), buttonSize)
180            right.button.setGeometry(geom.translated(-buttonSize.width(), 0))
181            margins.setLeft(buttonSize.width())
182
183        self.setTextMargins(margins)
184
185    def __onTriggered(self, action):
186        left, right = self.__actions
187        if left and action == left.action:
188            self.leftTriggered.emit()
189        elif right and action == right.action:
190            self.rightTriggered.emit()
Note: See TracBrowser for help on using the repository browser.