source: orange/Orange/OrangeCanvas/document/editlinksdialog.py @ 11448:eb79054d35a5

Revision 11448:eb79054d35a5, 24.8 KB checked in by Ales Erjavec <ales.erjavec@…>, 13 months ago (diff)

Fixed/added editlinksdialog docstrings.

Line 
1"""
2===========
3Link Editor
4===========
5
6An Dialog to edit links between two nodes in the scheme.
7
8"""
9
10from collections import namedtuple
11
12from xml.sax.saxutils import escape
13
14from PyQt4.QtGui import (
15    QApplication, QDialog, QVBoxLayout, QDialogButtonBox, QGraphicsScene,
16    QGraphicsView, QGraphicsWidget, QGraphicsRectItem,
17    QGraphicsLineItem, QGraphicsTextItem, QGraphicsLayoutItem,
18    QGraphicsLinearLayout, QGraphicsGridLayout, QGraphicsPixmapItem,
19    QGraphicsDropShadowEffect, QSizePolicy, QPalette, QPen,
20    QPainter, QIcon
21)
22
23from PyQt4.QtCore import (
24    Qt, QObject, QSize, QSizeF, QPointF, QRectF, qVersion
25)
26
27from PyQt4.QtCore import pyqtSignal as Signal
28
29from ..scheme import SchemeNode, SchemeLink, compatible_channels
30from ..registry import InputSignal, OutputSignal
31
32from ..resources import icon_loader
33
34# This is a special value defined in Qt4 but does not seem to be exported
35# by PyQt4
36QWIDGETSIZE_MAX = ((1 << 24) - 1)
37
38
39class EditLinksDialog(QDialog):
40    """
41    A dialog for editing links.
42
43    >>> dlg = EditLinksDialog()
44    >>> dlg.setNodes(file_node, test_learners_node)
45    >>> dlg.setLinks([(file_node.output_channel("Data"),
46    ...               (test_learners_node.input_channel("Data")])
47    >>> if dlg.exec_() == EditLinksDialog.Accpeted:
48    ...     new_links = dlg.links()
49    ...
50
51    """
52    def __init__(self, *args, **kwargs):
53        QDialog.__init__(self, *args, **kwargs)
54
55        self.setModal(True)
56
57        self.__setupUi()
58
59    def __setupUi(self):
60        layout = QVBoxLayout()
61
62        # Scene with the link editor.
63        self.scene = LinksEditScene()
64        self.view = QGraphicsView(self.scene)
65        self.view.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
66        self.view.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
67        self.view.setRenderHint(QPainter.Antialiasing)
68
69        self.scene.editWidget.geometryChanged.connect(self.__onGeometryChanged)
70
71        # Ok/Cancel/Clear All buttons.
72        buttons = QDialogButtonBox(QDialogButtonBox.Ok |
73                                   QDialogButtonBox.Cancel |
74                                   QDialogButtonBox.Reset,
75                                   Qt.Horizontal)
76
77        clear_button = buttons.button(QDialogButtonBox.Reset)
78        clear_button.setText(self.tr("Clear All"))
79
80        buttons.accepted.connect(self.accept)
81        buttons.rejected.connect(self.reject)
82        clear_button.clicked.connect(self.scene.editWidget.clearLinks)
83
84        layout.addWidget(self.view)
85        layout.addWidget(buttons)
86
87        self.setLayout(layout)
88        layout.setSizeConstraint(QVBoxLayout.SetFixedSize)
89
90        self.setSizeGripEnabled(False)
91
92    def setNodes(self, source_node, sink_node):
93        """
94        Set the source/sink nodes (:class:`.SchemeNode` instances)
95        between which to edit the links.
96
97        .. note:: This should be called before :func:`setLinks`.
98
99        """
100        self.scene.editWidget.setNodes(source_node, sink_node)
101
102    def setLinks(self, links):
103        """
104        Set a list of links to display between the source and sink
105        nodes. The `links` is a list of (`OutputSignal`, `InputSignal`)
106        tuples where the first element is an output signal of the source
107        node and the second an input signal of the sink node.
108
109        """
110        self.scene.editWidget.setLinks(links)
111
112    def links(self):
113        """
114        Return the links between the source and sink node.
115        """
116        return self.scene.editWidget.links()
117
118    def __onGeometryChanged(self):
119        size = self.scene.editWidget.size()
120        left, top, right, bottom = self.getContentsMargins()
121        self.view.setFixedSize(size.toSize() + \
122                               QSize(left + right + 4, top + bottom + 4))
123
124
125def find_item_at(scene, pos, order=Qt.DescendingOrder, type=None,
126                 name=None):
127    """
128    Find an object in a :class:`QGraphicsScene` `scene` at `pos`.
129    If `type` is not `None` the it must specify  the type of the item.
130    I `name` is not `None` it must be a name of the object
131    (`QObject.objectName()`).
132
133    """
134    items = scene.items(pos, Qt.IntersectsItemShape, order)
135    for item in items:
136        if type is not None and \
137                not isinstance(item, type):
138            continue
139
140        if name is not None and isinstance(item, QObject) and \
141                item.objectName() != name:
142            continue
143        return item
144    else:
145        return None
146
147
148class LinksEditScene(QGraphicsScene):
149    """
150    A :class:`QGraphicsScene` used by the :class:`LinkEditWidget`.
151    """
152    def __init__(self, *args, **kwargs):
153        QGraphicsScene.__init__(self, *args, **kwargs)
154
155        self.editWidget = LinksEditWidget()
156        self.addItem(self.editWidget)
157
158    findItemAt = find_item_at
159
160
161_Link = namedtuple(
162    "_Link",
163    ["output",    # OutputSignal
164     "input",     # InputSignal
165     "lineItem",  # QGraphicsLineItem connecting the input to output
166     ])
167
168
169class LinksEditWidget(QGraphicsWidget):
170    """
171    A Graphics Widget for editing the links between two nodes.
172    """
173    def __init__(self, *args, **kwargs):
174        QGraphicsWidget.__init__(self, *args, **kwargs)
175        self.setAcceptedMouseButtons(Qt.LeftButton | Qt.RightButton)
176
177        self.source = None
178        self.sink = None
179
180        # QGraphicsWidget/Items in the scene.
181        self.sourceNodeWidget = None
182        self.sourceNodeTitle = None
183        self.sinkNodeWidget = None
184        self.sinkNodeTitle = None
185
186        self.__links = []
187
188        self.__textItems = []
189        self.__iconItems = []
190        self.__tmpLine = None
191        self.__dragStartItem = None
192
193        self.setLayout(QGraphicsLinearLayout(Qt.Vertical))
194        self.layout().setContentsMargins(0, 0, 0, 0)
195
196    def removeItems(self, items):
197        """
198        Remove child items from the widget and scene.
199        """
200        scene = self.scene()
201        for item in items:
202            item.setParentItem(None)
203            if scene is not None:
204                scene.removeItem(item)
205
206    def clear(self):
207        """
208        Clear the editor state (source and sink nodes, channels ...).
209        """
210        if self.layout().count():
211            widget = self.layout().takeAt(0).graphicsItem()
212            self.removeItems([widget])
213
214        self.source = None
215        self.sink = None
216
217    def setNodes(self, source, sink):
218        """
219        Set the source/sink nodes (:class:`SchemeNode` instances) between
220        which to edit the links.
221
222        .. note:: Call this before :func:`setLinks`.
223
224        """
225        self.clear()
226
227        self.source = source
228        self.sink = sink
229
230        self.__updateState()
231
232    def setLinks(self, links):
233        """
234        Set a list of links to display between the source and sink
235        nodes. `links` must be a list of (`OutputSignal`, `InputSignal`)
236        tuples where the first element refers to the source node
237        and the second to the sink node (as set by `setNodes`).
238
239        """
240        self.clearLinks()
241        for output, input in links:
242            self.addLink(output, input)
243
244    def links(self):
245        """
246        Return the links between the source and sink node.
247        """
248        return [(link.output, link.input) for link in self.__links]
249
250    def mousePressEvent(self, event):
251        if event.button() == Qt.LeftButton:
252            startItem = find_item_at(self.scene(), event.pos(),
253                                     type=ChannelAnchor)
254            if startItem is not None:
255                # Start a connection line drag.
256                self.__dragStartItem = startItem
257                self.__tmpLine = None
258                event.accept()
259                return
260
261            lineItem = find_item_at(self.scene(), event.scenePos(),
262                                    type=QGraphicsLineItem)
263            if lineItem is not None:
264                # Remove a connection under the mouse
265                for link in self.__links:
266                    if link.lineItem == lineItem:
267                        self.removeLink(link.output, link.input)
268                event.accept()
269                return
270
271        QGraphicsWidget.mousePressEvent(self, event)
272
273    def mouseMoveEvent(self, event):
274        if event.buttons() & Qt.LeftButton:
275
276            downPos = event.buttonDownPos(Qt.LeftButton)
277            if not self.__tmpLine and self.__dragStartItem and \
278                    (downPos - event.pos()).manhattanLength() > \
279                        QApplication.instance().startDragDistance():
280                # Start a line drag
281                line = QGraphicsLineItem(self)
282                start = self.__dragStartItem.boundingRect().center()
283                start = self.mapFromItem(self.__dragStartItem, start)
284                line.setLine(start.x(), start.y(),
285                             event.pos().x(), event.pos().y())
286
287                pen = QPen(Qt.green, 4)
288                pen.setCapStyle(Qt.RoundCap)
289                line.setPen(pen)
290                line.show()
291
292                self.__tmpLine = line
293
294            if self.__tmpLine:
295                # Update the temp line
296                line = self.__tmpLine.line()
297                line.setP2(event.pos())
298                self.__tmpLine.setLine(line)
299
300        QGraphicsWidget.mouseMoveEvent(self, event)
301
302    def mouseReleaseEvent(self, event):
303        if event.button() == Qt.LeftButton and self.__tmpLine:
304            endItem = find_item_at(self.scene(), event.scenePos(),
305                                     type=ChannelAnchor)
306
307            if endItem is not None:
308                startItem = self.__dragStartItem
309                startChannel = startItem.channel()
310                endChannel = endItem.channel()
311                possible = False
312
313                # Make sure the drag was from input to output (or reversed) and
314                # not between input -> input or output -> output
315                if type(startChannel) != type(endChannel):
316                    if isinstance(startChannel, InputSignal):
317                        startChannel, endChannel = endChannel, startChannel
318
319                    possible = compatible_channels(startChannel, endChannel)
320
321                if possible:
322                    self.addLink(startChannel, endChannel)
323
324            self.scene().removeItem(self.__tmpLine)
325            self.__tmpLine = None
326            self.__dragStartItem = None
327
328        QGraphicsWidget.mouseReleaseEvent(self, event)
329
330    def addLink(self, output, input):
331        """
332        Add a link between `output` (:class:`OutputSignal`) and `input`
333        (:class:`InputSignal`).
334
335        """
336        if not compatible_channels(output, input):
337            return
338
339        if output not in self.source.output_channels():
340            raise ValueError("%r is not an output channel of %r" % \
341                             (output, self.source))
342
343        if input not in self.sink.input_channels():
344            raise ValueError("%r is not an input channel of %r" % \
345                             (input, self.sink))
346
347        if input.single:
348            # Remove existing link if it exists.
349            for s1, s2, _ in self.__links:
350                if s2 == input:
351                    self.removeLink(s1, s2)
352
353        line = QGraphicsLineItem(self)
354
355        source_anchor = self.sourceNodeWidget.anchor(output)
356        sink_anchor = self.sinkNodeWidget.anchor(input)
357
358        source_pos = source_anchor.boundingRect().center()
359        source_pos = self.mapFromItem(source_anchor, source_pos)
360
361        sink_pos = sink_anchor.boundingRect().center()
362        sink_pos = self.mapFromItem(sink_anchor, sink_pos)
363        line.setLine(source_pos.x(), source_pos.y(),
364                     sink_pos.x(), sink_pos.y())
365        pen = QPen(Qt.green, 4)
366        pen.setCapStyle(Qt.RoundCap)
367        line.setPen(pen)
368
369        self.__links.append(_Link(output, input, line))
370
371    def removeLink(self, output, input):
372        """
373        Remove a link between the `output` and `input` channels.
374        """
375        for link in list(self.__links):
376            if link.output == output and link.input == input:
377                self.scene().removeItem(link.lineItem)
378                self.__links.remove(link)
379                break
380        else:
381            raise ValueError("No such link {0.name!r} -> {1.name!r}." \
382                             .format(output, input))
383
384    def clearLinks(self):
385        """
386        Clear (remove) all the links.
387        """
388        for output, input, _ in list(self.__links):
389            self.removeLink(output, input)
390
391    def __updateState(self):
392        """
393        Update the widget with the new source/sink node signal descriptions.
394        """
395        widget = QGraphicsWidget()
396        widget.setLayout(QGraphicsGridLayout())
397
398        # Space between left and right anchors
399        widget.layout().setHorizontalSpacing(50)
400
401        left_node = EditLinksNode(self, direction=Qt.LeftToRight,
402                                  node=self.source)
403
404        left_node.setSizePolicy(QSizePolicy.MinimumExpanding,
405                                QSizePolicy.MinimumExpanding)
406
407        right_node = EditLinksNode(self, direction=Qt.RightToLeft,
408                                   node=self.sink)
409
410        right_node.setSizePolicy(QSizePolicy.MinimumExpanding,
411                                 QSizePolicy.MinimumExpanding)
412
413        left_node.setMinimumWidth(150)
414        right_node.setMinimumWidth(150)
415
416        widget.layout().addItem(left_node, 0, 0,)
417        widget.layout().addItem(right_node, 0, 1,)
418
419        title_template = "<center><b>{0}<b></center>"
420
421        left_title = GraphicsTextWidget(self)
422        left_title.setHtml(title_template.format(escape(self.source.title)))
423        left_title.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
424
425        right_title = GraphicsTextWidget(self)
426        right_title.setHtml(title_template.format(escape(self.sink.title)))
427        right_title.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
428
429        widget.layout().addItem(left_title, 1, 0,
430                                alignment=Qt.AlignHCenter | Qt.AlignTop)
431        widget.layout().addItem(right_title, 1, 1,
432                                alignment=Qt.AlignHCenter | Qt.AlignTop)
433
434        widget.setParentItem(self)
435
436        max_w = max(left_node.sizeHint(Qt.PreferredSize).width(),
437                    right_node.sizeHint(Qt.PreferredSize).width())
438
439        # fix same size
440        left_node.setMinimumWidth(max_w)
441        right_node.setMinimumWidth(max_w)
442        left_title.setMinimumWidth(max_w)
443        right_title.setMinimumWidth(max_w)
444
445        self.layout().addItem(widget)
446        self.layout().activate()
447
448        self.sourceNodeWidget = left_node
449        self.sinkNodeWidget = right_node
450        self.sourceNodeTitle = left_title
451        self.sinkNodeTitle = right_title
452
453    if qVersion() < "4.7":
454        geometryChanged = Signal()
455
456        def setGeometry(self, rect):
457            QGraphicsWidget.setGeometry(self, rect)
458            self.geometryChanged.emit()
459
460
461class EditLinksNode(QGraphicsWidget):
462    """
463    A Node representation with channel anchors.
464
465    `direction` specifies the layout (default `Qt.LeftToRight` will
466    have icon on the left and channels on the right).
467
468    """
469
470    def __init__(self, parent=None, direction=Qt.LeftToRight,
471                 node=None, icon=None, iconSize=None, **args):
472        QGraphicsWidget.__init__(self, parent, **args)
473        self.setAcceptedMouseButtons(Qt.NoButton)
474        self.__direction = direction
475
476        self.setLayout(QGraphicsLinearLayout(Qt.Horizontal))
477
478        # Set the maximum size, otherwise the layout can't grow beyond its
479        # sizeHint (and we need it to grow so the widget can grow and keep the
480        # contents centered vertically.
481        self.layout().setMaximumSize(QSizeF(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX))
482
483        self.setSizePolicy(QSizePolicy.MinimumExpanding,
484                           QSizePolicy.MinimumExpanding)
485
486        self.__iconSize = iconSize or QSize(64, 64)
487        self.__icon = icon
488
489        self.__iconItem = QGraphicsPixmapItem(self)
490        self.__iconLayoutItem = GraphicsItemLayoutItem(item=self.__iconItem)
491
492        self.__channelLayout = QGraphicsGridLayout()
493        self.__channelAnchors = []
494
495        if self.__direction == Qt.LeftToRight:
496            self.layout().addItem(self.__iconLayoutItem)
497            self.layout().addItem(self.__channelLayout)
498            channel_alignemnt = Qt.AlignRight
499
500        else:
501            self.layout().addItem(self.__channelLayout)
502            self.layout().addItem(self.__iconLayoutItem)
503            channel_alignemnt = Qt.AlignLeft
504
505        self.layout().setAlignment(self.__iconLayoutItem, Qt.AlignCenter)
506        self.layout().setAlignment(self.__channelLayout,
507                                   Qt.AlignVCenter | channel_alignemnt)
508
509        if node is not None:
510            self.setSchemeNode(node)
511
512    def setIconSize(self, size):
513        """
514        Set the icon size for the node.
515        """
516        if size != self.__iconSize:
517            self.__iconSize = QSize(size)
518            if self.__icon:
519                self.__iconItem.setPixmap(self.__icon.pixmap(size))
520                self.__iconLayoutItem.updateGeometry()
521
522    def iconSize(self):
523        """
524        Return the icon size.
525        """
526        return QSize(self.__iconSize)
527
528    def setIcon(self, icon):
529        """
530        Set the icon to display.
531        """
532        if icon != self.__icon:
533            self.__icon = QIcon(icon)
534            self.__iconItem.setPixmap(icon.pixmap(self.iconSize()))
535            self.__iconLayoutItem.updateGeometry()
536
537    def icon(self):
538        """
539        Return the icon.
540        """
541        return QIcon(self.__icon)
542
543    def setSchemeNode(self, node):
544        """
545        Set an instance of `SchemeNode`. The widget will be initialized
546        with its icon and channels.
547
548        """
549        self.node = node
550
551        if self.__direction == Qt.LeftToRight:
552            channels = node.output_channels()
553        else:
554            channels = node.input_channels()
555        self.channels = channels
556
557        loader = icon_loader.from_description(node.description)
558        icon = loader.get(node.description.icon)
559
560        self.setIcon(icon)
561
562        label_template = ('<div align="{align}">'
563                          '<b class="channelname">{name}</b><br/>'
564                          '<span class="typename">{typename}</span>'
565                          '</div>')
566
567        if self.__direction == Qt.LeftToRight:
568            align = "right"
569            label_alignment = Qt.AlignVCenter | Qt.AlignRight
570            anchor_alignment = Qt.AlignVCenter | Qt.AlignLeft
571            label_row = 0
572            anchor_row = 1
573        else:
574            align = "left"
575            label_alignment = Qt.AlignVCenter | Qt.AlignLeft
576            anchor_alignment = Qt.AlignVCenter | Qt.AlignLeft
577            label_row = 1
578            anchor_row = 0
579
580        self.__channelAnchors = []
581        grid = self.__channelLayout
582
583        for i, channel in enumerate(channels):
584            text = label_template.format(align=align,
585                                         name=escape(channel.name),
586                                         typename=escape(channel.type))
587
588            text_item = GraphicsTextWidget(self)
589            text_item.setHtml(text)
590            text_item.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
591
592            grid.addItem(text_item, i, label_row,
593                         alignment=label_alignment)
594
595            anchor = ChannelAnchor(self, channel=channel,
596                                   rect=QRectF(0, 0, 20, 20))
597
598            anchor.setBrush(self.palette().brush(QPalette.Mid))
599
600            layout_item = GraphicsItemLayoutItem(grid, item=anchor)
601            grid.addItem(layout_item, i, anchor_row,
602                         alignment=anchor_alignment)
603
604            if hasattr(channel, "description"):
605                text_item.setToolTip((channel.description))
606
607            self.__channelAnchors.append(anchor)
608
609    def anchor(self, channel):
610        """
611        Return the anchor item for the `channel` name.
612        """
613        for anchor in self.__channelAnchors:
614            if anchor.channel() == channel:
615                return anchor
616
617        raise ValueError(channel.name)
618
619    def paint(self, painter, option, widget=None):
620        painter.save()
621        palette = self.palette()
622        border = palette.brush(QPalette.Mid)
623        pen = QPen(border, 1)
624        pen.setCosmetic(True)
625        painter.setPen(pen)
626        painter.setBrush(palette.brush(QPalette.Window))
627        brect = self.boundingRect()
628        painter.drawRoundedRect(brect, 4, 4)
629        painter.restore()
630
631
632class GraphicsItemLayoutItem(QGraphicsLayoutItem):
633    """
634    A graphics layout that handles the position of a general QGraphicsItem
635    in a QGraphicsLayout. The items boundingRect is used as this items fixed
636    sizeHint and the item is positioned at the top left corner of the this
637    items geometry.
638
639    """
640
641    def __init__(self, parent=None, item=None, ):
642        self.__item = None
643
644        QGraphicsLayoutItem.__init__(self, parent, isLayout=False)
645
646        self.setOwnedByLayout(True)
647        self.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
648
649        if item is not None:
650            self.setItem(item)
651
652    def setItem(self, item):
653        self.__item = item
654        self.setGraphicsItem(item)
655
656    def setGeometry(self, rect):
657        # TODO: specifiy if the geometry should be set relative to the
658        # bounding rect top left corner
659        if self.__item:
660            self.__item.setPos(rect.topLeft())
661
662        QGraphicsLayoutItem.setGeometry(self, rect)
663
664    def sizeHint(self, which, constraint):
665        if self.__item:
666            return self.__item.boundingRect().size()
667        else:
668            return QGraphicsLayoutItem.sizeHint(self, which, constraint)
669
670
671class ChannelAnchor(QGraphicsRectItem):
672    """
673    A rectangular Channel Anchor indicator.
674    """
675    def __init__(self, parent=None, channel=None, rect=None, **kwargs):
676        QGraphicsRectItem.__init__(self, **kwargs)
677        self.setAcceptHoverEvents(True)
678        self.setAcceptedMouseButtons(Qt.NoButton)
679        self.__channel = None
680
681        if rect is None:
682            rect = QRectF(0, 0, 20, 20)
683
684        self.setRect(rect)
685
686        if channel:
687            self.setChannel(channel)
688
689        self.__shadow = QGraphicsDropShadowEffect(blurRadius=5,
690                                                  offset=QPointF(0, 0))
691        self.setGraphicsEffect(self.__shadow)
692        self.__shadow.setEnabled(False)
693
694    def setChannel(self, channel):
695        """
696        Set the channel description.
697        """
698        if channel != self.__channel:
699            self.__channel = channel
700
701            if hasattr(channel, "description"):
702                self.setToolTip(channel.description)
703            # TODO: Should also include name, type, flags, dynamic in the
704            #       tool tip as well as add visual clues to the anchor
705
706    def channel(self):
707        """
708        Return the channel description.
709        """
710        return self.__channel
711
712    def hoverEnterEvent(self, event):
713        self.__shadow.setEnabled(True)
714        QGraphicsRectItem.hoverEnterEvent(self, event)
715
716    def hoverLeaveEvent(self, event):
717        self.__shadow.setEnabled(False)
718        QGraphicsRectItem.hoverLeaveEvent(self, event)
719
720
721class GraphicsTextWidget(QGraphicsWidget):
722    """
723    A QGraphicsWidget subclass that manages a `QGraphicsTextItem`.
724    """
725
726    def __init__(self, parent=None, textItem=None):
727        QGraphicsLayoutItem.__init__(self, parent)
728        if textItem is None:
729            textItem = QGraphicsTextItem()
730
731        self.__textItem = textItem
732        self.__textItem.setParentItem(self)
733        self.__textItem.setPos(0, 0)
734
735        doc_layout = self.document().documentLayout()
736        doc_layout.documentSizeChanged.connect(self._onDocumentSizeChanged)
737
738    def sizeHint(self, which, constraint=QSizeF()):
739        # TODO: More sensible size hints.
740        # If the text is a plain text or html
741        # Check how QLabel.sizeHint works.
742
743        if which == Qt.PreferredSize:
744            return self.__textItem.boundingRect().size()
745        else:
746            return QGraphicsWidget.sizeHint(self, which, constraint)
747
748    def setGeometry(self, rect):
749        QGraphicsWidget.setGeometry(self, rect)
750        self.__textItem.setTextWidth(rect.width())
751
752    def setPlainText(self, text):
753        self.__textItem.setPlainText(text)
754        self.updateGeometry()
755
756    def setHtml(self, text):
757        self.__textItem.setHtml(text)
758
759    def adjustSize(self):
760        self.__textItem.adjustSize()
761        self.updateGeometry()
762
763    def setDefaultTextColor(self, color):
764        self.__textItem.setDefaultTextColor(color)
765
766    def document(self):
767        return self.__textItem.document()
768
769    def setDocument(self, doc):
770        doc_layout = self.document().documentLayout()
771        doc_layout.documentSizeChanged.disconnect(self._onDocumentSizeChanged)
772
773        self.__textItem.setDocument(doc)
774
775        doc_layout = self.document().documentLayout()
776        doc_layout.documentSizeChanged.connect(self._onDocumentSizeChanged)
777
778        self.updateGeometry()
779
780    def _onDocumentSizeChanged(self, size):
781        """The doc size has changed"""
782        self.updateGeometry()
Note: See TracBrowser for help on using the repository browser.