source: orange/Orange/OrangeCanvas/preview/previewbrowser.py @ 11212:67fb27ea57b1

Revision 11212:67fb27ea57b1, 9.7 KB checked in by Ales Erjavec <ales.erjavec@…>, 17 months ago (diff)

Elide the path text label if it is too long.

Line 
1"""
2Preview Browser Widget.
3
4"""
5
6from PyQt4.QtGui import (
7    QWidget, QLabel, QListView, QAction, QVBoxLayout, QHBoxLayout, QSizePolicy,
8    QStyleOption, QStylePainter
9)
10
11from PyQt4.QtSvg import QSvgWidget
12
13from PyQt4.QtCore import (
14    Qt, QSize, QByteArray, QModelIndex, QEvent
15)
16
17from PyQt4.QtCore import pyqtSignal as Signal
18
19from ..scheme.utils import check_type
20from ..gui.dropshadow import DropShadowFrame
21from . import previewmodel
22
23
24NO_PREVIEW_SVG = """
25
26"""
27
28
29# Default description template
30DESCRIPTION_TEMPLATE = u"""
31<h3 class=item-heading>{name}</h3>
32<p class=item-description>
33{description}
34</p>
35
36"""
37
38PREVIEW_SIZE = (440, 295)
39
40
41class LinearIconView(QListView):
42    def __init__(self, *args, **kwargs):
43        QListView.__init__(self, *args, **kwargs)
44
45        self.setViewMode(QListView.IconMode)
46        self.setWrapping(False)
47        self.setWordWrap(True)
48
49        self.setSelectionMode(QListView.SingleSelection)
50        self.setEditTriggers(QListView.NoEditTriggers)
51        self.setMovement(QListView.Static)
52        self.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
53        self.setSizePolicy(QSizePolicy.Expanding,
54                           QSizePolicy.Fixed)
55
56        self.setIconSize(QSize(120, 80))
57
58    def sizeHint(self):
59        if not self.model().rowCount():
60            return QSize(200, 140)
61        else:
62            scrollHint = self.horizontalScrollBar().sizeHint()
63            height = self.sizeHintForRow(0) + scrollHint.height()
64            _, top, _, bottom = self.getContentsMargins()
65            return QSize(200, height + top + bottom + self.verticalOffset())
66
67
68class TextLabel(QWidget):
69    """A plain text label widget with support for elided text.
70    """
71    def __init__(self, *args, **kwargs):
72        QWidget.__init__(self, *args, **kwargs)
73
74        self.setSizePolicy(QSizePolicy.Expanding,
75                           QSizePolicy.Preferred)
76
77        self.__text = ""
78        self.__textElideMode = Qt.ElideMiddle
79        self.__sizeHint = None
80        self.__alignment = Qt.AlignLeft | Qt.AlignVCenter
81
82    def setText(self, text):
83        """Set the `text` string to display.
84        """
85        check_type(text, basestring)
86        if self.__text != text:
87            self.__text = unicode(text)
88            self.__update()
89
90    def text(self):
91        """Return the text
92        """
93        return self.__text
94
95    def setTextElideMode(self, mode):
96        """Set elide mode (`Qt.TextElideMode`)
97        """
98        if self.__textElideMode != mode:
99            self.__textElideMode = mode
100            self.__update()
101
102    def elideMode(self):
103        return self.__elideMode
104
105    def setAlignment(self, align):
106        """Set text alignment (`Qt.Alignment`).
107        """
108        if self.__alignment != align:
109            self.__alignment = align
110            self.__update()
111
112    def sizeHint(self):
113        if self.__sizeHint is None:
114            option = QStyleOption()
115            option.initFrom(self)
116            metrics = option.fontMetrics
117
118            self.__sizeHint = QSize(200, metrics.height())
119
120        return self.__sizeHint
121
122    def paintEvent(self, event):
123        painter = QStylePainter(self)
124        option = QStyleOption()
125        option.initFrom(self)
126
127        rect = option.rect
128        metrics = option.fontMetrics
129        text = metrics.elidedText(self.__text, self.__textElideMode,
130                                  rect.width())
131        painter.drawItemText(rect, self.__alignment,
132                             option.palette, self.isEnabled(), text,
133                             self.foregroundRole())
134        painter.end()
135
136    def changeEvent(self, event):
137        if event.type() == QEvent.FontChange:
138            self.__update()
139
140        return QWidget.changeEvent(self, event)
141
142    def __update(self):
143        self.__sizeHint = None
144        self.updateGeometry()
145        self.update()
146
147
148class PreviewBrowser(QWidget):
149    """A Preview Browser for recent/premade scheme selection.
150    """
151    # Emitted when the current previewed item changes
152    currentIndexChanged = Signal(int)
153
154    def __init__(self, *args):
155        QWidget.__init__(self, *args)
156        self.__model = None
157        self.__currentIndex = -1
158        self.__template = DESCRIPTION_TEMPLATE
159        self.__setupUi()
160
161    def __setupUi(self):
162        vlayout = QVBoxLayout()
163        vlayout.setContentsMargins(0, 0, 0, 0)
164        top_layout = QHBoxLayout()
165        top_layout.setContentsMargins(12, 12, 12, 12)
166
167        # Top row with full text description and a large preview
168        # image.
169        self.__label = QLabel(self, objectName="description-label",
170                              wordWrap=True,
171                              alignment=Qt.AlignTop | Qt.AlignLeft)
172
173        self.__label.setWordWrap(True)
174        self.__label.setFixedSize(220, PREVIEW_SIZE[1])
175
176        self.__image = QSvgWidget(self, objectName="preview-image")
177        self.__image.setFixedSize(*PREVIEW_SIZE)
178
179        self.__imageFrame = DropShadowFrame(self)
180        self.__imageFrame.setWidget(self.__image)
181
182        # Path text below the description and image
183        path_layout = QHBoxLayout()
184        path_layout.setContentsMargins(12, 0, 12, 0)
185        path_label = QLabel("<b>{0!s}</b>".format(self.tr("Path:")), self,
186                            objectName="path-label")
187
188        self.__path = TextLabel(self, objectName="path-text")
189
190        path_layout.addWidget(path_label)
191        path_layout.addWidget(self.__path)
192
193        self.__selectAction = \
194            QAction(self.tr("Select"), self,
195                    objectName="select-action",
196                    )
197
198        top_layout.addWidget(self.__label, 1,
199                             alignment=Qt.AlignTop | Qt.AlignLeft)
200        top_layout.addWidget(self.__image, 1,
201                             alignment=Qt.AlignTop | Qt.AlignRight)
202
203        vlayout.addLayout(top_layout)
204        vlayout.addLayout(path_layout)
205
206        # An list view with small preview icons.
207        self.__previewList = LinearIconView(objectName="preview-list-view")
208
209        vlayout.addWidget(self.__previewList)
210        self.setLayout(vlayout)
211
212    def setModel(self, model):
213        """Set the item model for preview.
214        """
215        if self.__model != model:
216            if self.__model:
217                s_model = self.__previewList.selectionModel()
218                s_model.selectionChanged.disconnect(self.__onSelectionChanged)
219                self.__model.dataChanged.disconnect(self.__onDataChanged)
220
221            self.__model = model
222            self.__previewList.setModel(model)
223
224            if model:
225                s_model = self.__previewList.selectionModel()
226                s_model.selectionChanged.connect(self.__onSelectionChanged)
227                self.__model.dataChanged.connect(self.__onDataChanged)
228
229            if model and model.rowCount():
230                self.setCurrentIndex(0)
231
232    def model(self):
233        """Return the item model.
234        """
235        return self.__model
236
237    def setPreviewDelegate(self, delegate):
238        """Set the delegate to render the preview images.
239        """
240        raise NotImplementedError
241
242    def setDescriptionTemplate(self, template):
243        self.__template = template
244        self.__update()
245
246    def setCurrentIndex(self, index):
247        """Set the selected preview item index.
248        """
249        if self.__model is not None and self.__model.rowCount():
250            index = min(index, self.__model.rowCount() - 1)
251            index = self.__model.index(index, 0)
252            sel_model = self.__previewList.selectionModel()
253            # This emits selectionChanged signal and triggers
254            # __onSelectionChanged, currentIndex is updated there.
255            sel_model.select(index, sel_model.ClearAndSelect)
256
257        elif self.__currentIndex != -1:
258            self.__currentIndex = -1
259            self.__update()
260            self.currentIndexChanged.emit(-1)
261
262    def currentIndex(self):
263        """Return the current selected index.
264        """
265        return self.__currentIndex
266
267    def __onSelectionChanged(self, *args):
268        """Selected item in the preview list has changed.
269        Set the new description and large preview image.
270
271        """
272        rows = self.__previewList.selectedIndexes()
273        if rows:
274            index = rows[0]
275            self.__currentIndex = index.row()
276        else:
277            index = QModelIndex()
278            self.__currentIndex = -1
279
280        self.__update()
281        self.currentIndexChanged.emit(self.__currentIndex)
282
283    def __onDataChanged(self, topleft, bottomRight):
284        """Data changed, update the preview if current index in the changed
285        range.
286
287        """
288        if self.__currentIndex <= topleft.row() and \
289                self.__currentIndex >= bottomRight.row():
290            self.__update()
291
292    def __update(self):
293        """Update the description.
294        """
295        if self.__currentIndex != -1:
296            index = self.model().index(self.__currentIndex, 0)
297        else:
298            index = QModelIndex()
299
300        if not index.isValid():
301            description = ""
302            name = ""
303            path = ""
304            svg = NO_PREVIEW_SVG
305        else:
306            description = unicode(index.data(Qt.WhatsThisRole).toString())
307            if not description:
308                description = "No description."
309
310            name = unicode(index.data(Qt.DisplayRole).toString())
311            if not name:
312                name = "Untitled"
313
314            path = unicode(index.data(Qt.StatusTipRole).toString())
315
316            svg = unicode(index.data(previewmodel.ThumbnailSVGRole).toString())
317
318        desc_text = self.__template.format(description=description, name=name)
319
320        self.__label.setText(desc_text)
321
322        self.__path.setText(path)
323
324        if not svg:
325            svg = NO_PREVIEW_SVG
326
327        if svg:
328            self.__image.load(QByteArray(svg.encode("utf-8")))
Note: See TracBrowser for help on using the repository browser.