source: orange/Orange/OrangeCanvas/preview/previewmodel.py @ 11413:98d8cc8906c4

Revision 11413:98d8cc8906c4, 4.9 KB checked in by Ales Erjavec <ales.erjavec@…>, 13 months ago (diff)

Replaced QTimer.singleShot with a QTimer instance to schedule preview item updates.

QTimer.singleShot event can outlive the PreviewModel causing a RuntimeError.

Line 
1"""
2Preview item model.
3"""
4
5import logging
6
7from PyQt4.QtGui import (
8    QStandardItemModel, QStandardItem, QStyledItemDelegate, QIcon
9)
10
11from PyQt4.QtCore import Qt, QTimer
12
13from . import scanner
14
15log = logging.getLogger(__name__)
16
17# Preview Data Roles
18####################
19
20# Name of the item, (same as `Qt.DisplayRole`)
21NameRole = Qt.DisplayRole
22
23# Items description (items long description)
24DescriptionRole = Qt.UserRole + 1
25
26# Items url/path (where previewed resource is located).
27PathRole = Qt.UserRole + 2
28
29# Items preview SVG contents string
30ThumbnailSVGRole = Qt.UserRole + 3
31
32
33UNKNOWN_SVG = \
34"""<?xml version="1.0" encoding="UTF-8" standalone="no"?>
35<svg width="161.8mm" height="100.0mm"
36 xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
37 version="1.2" baseProfile="tiny">
38</svg>
39"""
40
41
42class PreviewModel(QStandardItemModel):
43    """A model for preview items.
44    """
45
46    def __init__(self, parent=None, items=None):
47        QStandardItemModel.__init__(self, parent)
48
49        if items is not None:
50            self.insertColumn(0, items)
51
52        self.__timer = QTimer(self)
53
54    def delayedScanUpdate(self, delay=10):
55        """Run a delayed preview item scan update.
56        """
57        def iter_update(items):
58            for item in items:
59                try:
60                    scanner.scan_update(item)
61                except Exception:
62                    log.error("An unexpected error occurred while "
63                              "scanning %r.", unicode(item.text()),
64                              exc_info=True)
65                    item.setEnabled(False)
66                yield
67
68        items = [self.item(i) for i in range(self.rowCount())]
69
70        iter_scan = iter_update(items)
71
72        def process_one():
73            try:
74                iter_scan.next()
75            except StopIteration:
76                self.__timer.timeout.disconnect(process_one)
77                self.__timer.stop()
78
79        self.__timer.timeout.connect(process_one)
80        self.__timer.start(delay)
81
82
83class PreviewItem(QStandardItem):
84    """A preview item.
85    """
86    def __init__(self, name=None, description=None, thumbnail=None,
87                 icon=None, path=None):
88        QStandardItem.__init__(self)
89
90        self.__name = ""
91
92        if name is None:
93            name = "Untitled"
94
95        self.setName(name)
96
97        if description is None:
98            description = "No description."
99        self.setDescription(description)
100
101        if thumbnail is None:
102            thumbnail = UNKNOWN_SVG
103        self.setThumbnail(thumbnail)
104
105        if icon is not None:
106            self.setIcon(icon)
107
108        if path is not None:
109            self.setPath(path)
110
111    def name(self):
112        """Return the name (title) of the item (same as `text()`.
113        """
114        return self.__name
115
116    def setName(self, value):
117        """Set the item name. `value` if not empty will be used as
118        the items DisplayRole otherwise an 'untitled' placeholder will
119        be used.
120
121        """
122        self.__name = value
123
124        if not value:
125            self.setText("untitled")
126        else:
127            self.setText(value)
128
129    def description(self):
130        """Return the detailed description for the item.
131
132        This is stored as `DescriptionRole`, if no data is set then
133        return the string for `WhatsThisRole`.
134
135        """
136        desc = self.data(DescriptionRole)
137
138        if desc.isValid():
139            return desc.toString()
140
141        whatsthis = self.data(Qt.WhatsThisRole)
142        return whatsthis.toString()
143
144    def setDescription(self, description):
145        self.setData(description, DescriptionRole)
146        self.setWhatsThis(description)
147
148    def thumbnail(self):
149        """Return the thumbnail SVG string for the preview item.
150
151        This is stored as `ThumbnailSVGRole`
152        """
153        thumb = self.data(ThumbnailSVGRole)
154        if thumb.isValid():
155            return thumb.toString()
156
157    def setThumbnail(self, thumbnail):
158        """Set the thumbnail SVG contents as a string.
159
160        When set it also overrides the icon role.
161
162        """
163        self.setData(thumbnail, ThumbnailSVGRole)
164
165        # TODO: how to set the icon from the svg contents string
166        # without writing it to disc
167        import tempfile
168        with tempfile.NamedTemporaryFile(
169                prefix="thumbnail", suffix=".svg", delete=False
170                ) as f:
171            f.write(thumbnail.encode("utf-8"))
172            f.flush()
173            icon = QIcon(f.name)
174            self.setIcon(icon)
175
176    def path(self):
177        """Return the path item data.
178        """
179        return self.data(PathRole).toString()
180
181    def setPath(self, path):
182        """Set the path data of the item.
183
184        .. note:: This also sets the Qt.StatusTipRole
185
186        """
187        self.setData(path, PathRole)
188        self.setStatusTip(path)
189        self.setToolTip(path)
190
191
192class PreviewItemDelegate(QStyledItemDelegate):
193    def __init__(self, ):
194        QStyledItemDelegate.__init__(self,)
Note: See TracBrowser for help on using the repository browser.