source: orange/Orange/OrangeCanvas/preview/previewbrowser.py @ 11114:16262014a6ea

Revision 11114:16262014a6ea, 7.1 KB checked in by Ales Erjavec <ales.erjavec@…>, 19 months ago (diff)

Added preview browser widget and dialog.

Line 
1"""
2Preview Browser Widget.
3
4"""
5
6from PyQt4.QtGui import (
7    QWidget, QLabel, QListView, QAction, QVBoxLayout, QHBoxLayout, QSizePolicy
8)
9
10from PyQt4.QtSvg import QSvgWidget
11
12from PyQt4.QtCore import (
13    Qt, QSize, QByteArray, QModelIndex
14)
15
16from PyQt4.QtCore import pyqtSignal as Signal
17
18from ..gui.dropshadow import DropShadowFrame
19from . import previewmodel
20
21
22NO_PREVIEW_SVG = """
23
24"""
25
26
27# Default description template
28DESCRIPTION_TEMPLATE = """
29<h3 class=item-heading>{name}</h3>
30<p class=item-description>
31{description}
32</p>
33
34"""
35
36PREVIEW_SIZE = (440, 295)
37
38
39class LinearIconView(QListView):
40    def __init__(self, *args, **kwargs):
41        QListView.__init__(self, *args, **kwargs)
42
43        self.setViewMode(QListView.IconMode)
44        self.setWrapping(False)
45        self.setSelectionMode(QListView.SingleSelection)
46        self.setEditTriggers(QListView.NoEditTriggers)
47        self.setMovement(QListView.Static)
48        self.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
49        self.setSizePolicy(QSizePolicy.Expanding,
50                           QSizePolicy.Fixed)
51
52        self.setIconSize(QSize(120, 80))
53
54    def sizeHint(self):
55        if not self.model().rowCount():
56            return QSize(200, 140)
57        else:
58            height = self.sizeHintForRow(0)
59            _, top, _, bottom = self.getContentsMargins()
60            return QSize(200, height + top + bottom + self.verticalOffset())
61
62
63class PreviewBrowser(QWidget):
64    """A Preview Browser for recent/premade scheme selection.
65    """
66    # Emitted when the current previewed item changes
67    currentIndexChanged = Signal(int)
68
69    def __init__(self, *args):
70        QWidget.__init__(self, *args)
71        self.__model = None
72        self.__currentIndex = -1
73        self.__template = DESCRIPTION_TEMPLATE
74        self.__setupUi()
75
76    def __setupUi(self):
77        vlayout = QVBoxLayout()
78        vlayout.setContentsMargins(0, 0, 0, 0)
79        top_layout = QHBoxLayout()
80        top_layout.setContentsMargins(12, 12, 12, 12)
81
82        # Top row with full text description and a large preview
83        # image.
84        self.__label = QLabel(self, objectName="description-label",
85                              wordWrap=True,
86                              alignment=Qt.AlignTop | Qt.AlignLeft)
87
88        self.__label.setWordWrap(True)
89        self.__label.setFixedSize(220, PREVIEW_SIZE[1])
90
91        self.__image = QSvgWidget(self, objectName="preview-image")
92        self.__image.setFixedSize(*PREVIEW_SIZE)
93
94        self.__imageFrame = DropShadowFrame(self)
95        self.__imageFrame.setWidget(self.__image)
96
97        self.__path = QLabel(self, objectName="path-label")
98        self.__path.setWordWrap(False)
99        self.__path.setContentsMargins(12, 0, 12, 0)
100
101        self.__selectAction = \
102            QAction(self.tr("Select"), self,
103                    objectName="select-action",
104                    )
105
106        top_layout.addWidget(self.__label, 1,
107                             alignment=Qt.AlignTop | Qt.AlignLeft)
108        top_layout.addWidget(self.__image, 1,
109                             alignment=Qt.AlignTop | Qt.AlignRight)
110
111        vlayout.addLayout(top_layout)
112        vlayout.addWidget(self.__path)
113
114        # An list view with small preview icons.
115        self.__previewList = LinearIconView(objectName="preview-list-view")
116
117        vlayout.addWidget(self.__previewList)
118        self.setLayout(vlayout)
119
120    def setModel(self, model):
121        """Set the item model for preview.
122        """
123        if self.__model != model:
124            if self.__model:
125                s_model = self.__previewList.selectionModel()
126                s_model.selectionChanged.disconnect(self.__onSelectionChanged)
127                self.__model.dataChanged.disconnect(self.__onDataChanged)
128
129            self.__model = model
130            self.__previewList.setModel(model)
131
132            if model:
133                s_model = self.__previewList.selectionModel()
134                s_model.selectionChanged.connect(self.__onSelectionChanged)
135                self.__model.dataChanged.connect(self.__onDataChanged)
136
137            if model and model.rowCount():
138                self.setCurrentIndex(0)
139
140    def model(self):
141        """Return the item model.
142        """
143        return self.__model
144
145    def setPreviewDelegate(self, delegate):
146        """Set the delegate to render the preview images.
147        """
148        raise NotImplementedError
149
150    def setDescriptionTemplate(self, template):
151        self.__template = template
152        self.__update()
153
154    def setCurrentIndex(self, index):
155        """Set the selected preview item index.
156        """
157        if self.__model is not None and self.__model.rowCount():
158            index = min(index, self.__model.rowCount() - 1)
159            index = self.__model.index(index, 0)
160            sel_model = self.__previewList.selectionModel()
161            # This emits selectionChanged signal and triggers
162            # __onSelectionChanged, currentIndex is updated there.
163            sel_model.select(index, sel_model.ClearAndSelect)
164
165        elif self.__currentIndex != -1:
166            self.__currentIndex = -1
167            self.__update()
168            self.currentIndexChanged.emit(-1)
169
170    def currentIndex(self):
171        """Return the current selected index.
172        """
173        return self.__currentIndex
174
175    def __onSelectionChanged(self, *args):
176        """Selected item in the preview list has changed.
177        Set the new description and large preview image.
178
179        """
180        rows = self.__previewList.selectedIndexes()
181        if rows:
182            index = rows[0]
183            self.__currentIndex = index.row()
184        else:
185            index = QModelIndex()
186            self.__currentIndex = -1
187
188        self.__update()
189        self.currentIndexChanged.emit(self.__currentIndex)
190
191    def __onDataChanged(self, topleft, bottomRight):
192        """Data changed, update the preview if current index in the changed
193        range.
194
195        """
196        if self.__currentIndex <= topleft.row() and \
197                self.__currentIndex >= bottomRight.row():
198            self.__update()
199
200    def __update(self):
201        """Update the description.
202        """
203        if self.__currentIndex != -1:
204            index = self.model().index(self.__currentIndex, 0)
205        else:
206            index = QModelIndex()
207
208        if not index.isValid():
209            description = ""
210            name = ""
211            path = ""
212            svg = NO_PREVIEW_SVG
213        else:
214            description = unicode(index.data(Qt.WhatsThisRole).toString())
215            if not description:
216                description = "No description."
217
218            name = unicode(index.data(Qt.DisplayRole).toString())
219            if not name:
220                name = "Untitled"
221
222            path = unicode(index.data(Qt.StatusTipRole).toString())
223
224            svg = unicode(index.data(previewmodel.ThumbnailSVGRole).toString())
225
226        desc_text = self.__template.format(description=description, name=name)
227
228        self.__label.setText(desc_text)
229
230        path_text = "<b>Path:</b><div class=item-path>{0}</div>".format(path)
231        self.__path.setText(path_text)
232
233        if not svg:
234            svg = NO_PREVIEW_SVG
235
236        if svg:
237            self.__image.load(QByteArray(svg))
Note: See TracBrowser for help on using the repository browser.