def stamp_image(image, expression_str, position, feature): painter = QPainter(image) data = QgsExpression.replaceExpressionText(expression_str, feature, None) if not data: return image data = data.replace(r"\n", "<br>") style = """ body { color: yellow; } """ doc = QTextDocument() doc.setDefaultStyleSheet(style) data = "<body>{}</body>".format(data) doc.setHtml(data) point = QPointF(20, 20) # Wrap the text so we don't go crazy if doc.size().width() > 300: doc.setTextWidth(300) if position == "top-left": point = QPointF(20, 20) elif position == "top-right": x = image.width() - 20 - doc.size().width() point = QPointF(x, 20) elif position == "bottom-left": point = QPointF(20, image.height() - 20 - doc.size().height()) elif position == "bottom-right": x = image.width() - 20 - doc.size().width() y = image.height() - 20 - doc.size().height() point = QPointF(x, y) painter.translate(point) doc.drawContents(painter) return image
class FeatureShape(LabeledPolygonShape): def draw(self, painter, xMap, yMap, canvasRect): self._setup_painter(painter) self._set_outer_pen_and_brush(painter, xMap, yMap) rtmin = self.item.rtmin rtmax = self.item.rtmax mzmin = self.item.mzmin mzmax = self.item.mzmax self._draw_polygon(painter, xMap, yMap, (rtmin, rtmax, mzmin, mzmax)) self._set_inner_pen_and_brush(painter, xMap, yMap) for mass_trace in self.item.mass_traces: self._draw_polygon(painter, xMap, yMap, mass_trace) if self.label is not None: self._draw_label(painter, xMap, yMap) def _draw_label(self, painter, xMap, yMap): self.text = QTextDocument() self.text.setDefaultStyleSheet("""div { color: rgb(%d, %d, %d); }""" % self.color) self.text.setHtml("<div>%s</div>" % (self.label, )) x0 = xMap.transform(self.item.rtmax) # y0: height between m0 and m1 masstrace if m1 exists, else at height of m0 yi = sorted(m.mzmin for m in self.item.mass_traces) if len(yi) >= 2: y0 = yMap.transform(0.5 * yi[0] + 0.5 * yi[1]) else: y0 = yMap.transform(yi[0]) h = self.text.size().height() painter.translate(x0, y0 - 0.5 * h) self.text.drawContents(painter)
def show(cursor, pos=None, num_lines=6): """Displays a tooltip showing part of the cursor's Document. If the cursor has a selection, those blocks are displayed. Otherwise, num_lines lines are displayed. If pos is not given, the global mouse position is used. """ block = cursor.document().findBlock(cursor.selectionStart()) c2 = QTextCursor(block) if cursor.hasSelection(): c2.setPosition(cursor.selectionEnd(), QTextCursor.KeepAnchor) c2.movePosition(QTextCursor.EndOfBlock, QTextCursor.KeepAnchor) else: c2.movePosition(QTextCursor.NextBlock, QTextCursor.KeepAnchor, num_lines) data = textformats.formatData('editor') doc = QTextDocument() font = QFont(data.font) font.setPointSizeF(font.pointSizeF() * .8) doc.setDefaultFont(font) doc.setPlainText(c2.selection().toPlainText()) if metainfo.info(cursor.document()).highlighting: highlighter.highlight(doc, state=tokeniter.state(block)) size = doc.size().toSize() + QSize(8, -4) pix = QPixmap(size) pix.fill(data.baseColors['background']) doc.drawContents(QPainter(pix)) label = QLabel() label.setPixmap(pix) label.setStyleSheet("QLabel { border: 1px solid #777; }") label.resize(size) widgets.customtooltip.show(label, pos)
def displayAnnotation(self, geom, message): ''' Display a specific message in the centroid of a specific geometry ''' centroid = geom.centroid() # clean previous annotations: for annotation in self.annotations: try: scene = annotation.scene() if scene: scene.removeItem(annotation) except: # annotation can be erased by QGIS interface pass self.annotations = [] # build annotation textDoc = QTextDocument(message) item = QgsTextAnnotationItem(self.iface.mapCanvas()) item.setMapPosition(centroid.asPoint()) item.setFrameSize(textDoc.size()) item.setDocument(textDoc) item.update() # add to annotations self.annotations.append(item) # center in the centroid self.iface.mapCanvas().setCenter(centroid.asPoint()) self.iface.mapCanvas().zoomScale(float(self.defaultZoomScale)) self.iface.mapCanvas().refresh()
def sizeHint(self, option, index): options = QStyleOptionViewItemV4(option) self.initStyleOption(options, index) doc = QTextDocument() doc.setHtml(options.text) doc.setTextWidth(options.rect.width()) return QtCore.QSize(doc.idealWidth(), doc.size().height())
def sizeHint(self, option, index): options = QStyleOptionViewItemV4(option) self.initStyleOption(options,index) doc = QTextDocument() doc.setHtml(options.text) doc.setTextWidth(options.rect.width()) return QtCore.QSize(doc.idealWidth(), doc.size().height())
def sizeHint(self, option, index): optionV4 = QStyleOptionViewItemV4(option) self.initStyleOption(optionV4, index) doc = QTextDocument() doc.setHtml(optionV4.text) doc.setTextWidth(optionV4.rect.width()) return QSize(doc.idealWidth(), max(doc.size().height(), optionV4.decorationSize.height()))
def sizeHint(self, option, index): """Calculate the needed size.""" options = QStyleOptionViewItemV4(option) self.initStyleOption(options, index) doc = QTextDocument() doc.setHtml(options.text) doc.setTextWidth(options.rect.width()) return QSize(doc.idealWidth(), doc.size().height())
def sizeHint(self, option, index): """QStyledItemDelegate.sizeHint implementation """ options = QStyleOptionViewItemV4(option) self.initStyleOption(options, index) doc = QTextDocument() doc.setDocumentMargin(1) # bad long (multiline) strings processing doc.setTextWidth(options.rect.width()) doc.setHtml(options.text) return QSize(doc.idealWidth(), doc.size().height())
def sizeHint(self, option, index): """QStyledItemDelegate.sizeHint implementation """ options = QStyleOptionViewItemV4(option) self.initStyleOption(options,index) doc = QTextDocument() doc.setDocumentMargin(1) # bad long (multiline) strings processing doc.setTextWidth(options.rect.width()) doc.setHtml(options.text) return QSize(doc.idealWidth(), doc.size().height())
def sizeHint(self, option1, index): # option.rect is a zero rect width = self.parent().columnWidth(index.column()) if index.data(Qt.DisplayRole).type() == QMetaType.Double: return QSize(width, 18) option = QStyleOptionViewItemV4(option1) self.initStyleOption(option, index) if not option.text: iconSize = option.icon.actualSize(QSize(32, 32)) return QSize(32, max(32, iconSize.height() + 4)) doc = QTextDocument() doc.setHtml(option.text) doc.setTextWidth(self._preferredMessageWidth(width)) return QSize(doc.idealWidth(), max(32, doc.size().height() + 4))
class PeakRangeShape(LabeledPolygonShape): def draw(self, painter, xMap, yMap, canvasRect): self._setup_painter(painter) self._set_inner_pen_and_brush(painter, xMap, yMap) self._draw_polygon(painter, xMap, yMap, self.item) if self.label is not None: self._draw_label(painter, xMap, yMap) def _draw_label(self, painter, xMap, yMap): self.text = QTextDocument() self.text.setDefaultStyleSheet("""div { color: rgb(%d, %d, %d); }""" % self.color) self.text.setHtml("<div>%s</div>" % (self.label, )) x0 = xMap.transform(self.item.rtmax) # y0: height between m0 and m1 masstrace if m1 exists, else at height of m0 y0 = yMap.transform(0.5 * self.item.mzmin + 0.5 * self.item.mzmax) h = self.text.size().height() painter.translate(x0, y0 - 0.5 * h) self.text.drawContents(painter)
class MessageItemDelegate(QStyledItemDelegate): def __init__(self, parentView, logger, column=None, margin=50): super(MessageItemDelegate, self).__init__(parentView) self.logger = logger # We need that to receive mouse move events in editorEvent parentView.setMouseTracking(True) # Revert the mouse cursor when the mouse isn't over # an item but still on the view widget parentView.viewportEntered.connect(self.unsetParentCursor) self.document = QTextDocument() self.mouseOverDocument = self.document self.mouseOverDocumentRow = -1 self.mouseOverOption = None self.lastTextPos = QPoint(0, 0) self._editIndex = None self._editor = None self._column = column self._margin = margin ownGradient = QLinearGradient(0, 0, 0, 10) ownGradient.setColorAt(0, QColor(229, 239, 254)) ownGradient.setColorAt(1, QColor(182, 208, 251)) self._ownBrush = QBrush(ownGradient) self._ownPenColor = QColor(104, 126, 164) otherGradient = QLinearGradient(0, 0, 0, 10) otherGradient.setColorAt(0, QColor(248, 248, 248)) otherGradient.setColorAt(1, QColor(200, 200, 200)) self._otherBrush = QBrush(otherGradient) self._otherPenColor = QColor(153, 153, 153) self._timeFont = QFont("default", 12, QFont.Bold) self.closeEditor.connect(self.editorClosing) self._rowHeights = {} @loggingSlot() def unsetParentCursor(self): self.parent().unsetCursor() def setEditIndex(self, modelIndex): self._editIndex = modelIndex def getEditIndex(self): return self._editIndex @loggingSlot(QWidget, QAbstractItemDelegate.EndEditHint) def editorClosing(self, _editor, _hint): self._editor = None self.setEditIndex(None) def getEditor(self): return self._editor def createEditor(self, parent, option_, modelIndex): self.setEditIndex(modelIndex) option = QStyleOptionViewItemV4(option_) self.initStyleOption(option, modelIndex) text = QString(option.text) editorWidget = EditorWidget(parent) editor = ItemEditor(text, self._preferredMessageWidth(option.rect.width()), editorWidget) editorWidget.setItemEditor(editor) messageRect = self._getMessageRect(option, editor.document(), modelIndex, relativeToItem=True) pos = messageRect.topLeft() editor.move(pos) editor.resize(messageRect.size()) self._editor = editorWidget return editorWidget def setModelData(self, *_args, **_kwargs): pass def _preferredMessageWidth(self, textRectWidth): return textRectWidth - self._margin def _getMessageRect(self, option, doc, modelIndex, relativeToItem=False): rightAligned = modelIndex.data(ChatMessagesModel.OWN_MESSAGE_ROLE).toBool() statusIcon = modelIndex.data(ChatMessagesModel.STATUS_ICON_ROLE) hasStatusIcon = statusIcon != None and not statusIcon.isNull() textRect = option.rect documentWidth = doc.idealWidth() if rightAligned: xOffset = textRect.width() - documentWidth - 3 if hasStatusIcon: xOffset -= 20 else: xOffset = 3 if hasStatusIcon: xOffset += 20 height = doc.size().height() if modelIndex.row() not in self._rowHeights: self._rowHeights[modelIndex.row()] = height elif self._rowHeights[modelIndex.row()] != height: self._rowHeights[modelIndex.row()] = height self.sizeHintChanged.emit(modelIndex) if height < 32: # vertically center yOffset = (32. - height) / 2 + 1 else: yOffset = 0 textPos = QPoint(0,0) if relativeToItem else textRect.topLeft() textPos += QPoint(xOffset, yOffset) return QRect(textPos, QSize(documentWidth, height)) def _paintTime(self, painter, option, modelIndex): if modelIndex.column() != 1: return # total rect for us to paint in textRect = option.rect rtime, _ok = modelIndex.data(Qt.DisplayRole).toDouble() timeString = formatTime(localtime(rtime)) painter.save() painter.setRenderHint(QPainter.Antialiasing) painter.translate(textRect.topLeft()) painter.setFont(self._timeFont) textWidth = painter.fontMetrics().width(timeString) painter.drawText((textRect.size().width() - textWidth) / 2, 13, timeString) painter.restore() def paint(self, painter, option1, modelIndex): if self._column is not None and modelIndex.column() != self._column: return super(MessageItemDelegate, self).paint(painter, option1, modelIndex) option = QStyleOptionViewItemV4(option1) self.initStyleOption(option, modelIndex) if modelIndex.data(Qt.DisplayRole).type() == QMetaType.Double: # this is a time item self._paintTime(painter, option, modelIndex) return text = QString(option.text) if not text: option1.decorationAlignment = Qt.AlignLeft return super(MessageItemDelegate, self).paint(painter, option1, modelIndex) rightAligned = modelIndex.data(ChatMessagesModel.OWN_MESSAGE_ROLE).toBool() selected = (int(option.state) & int(QStyle.State_Selected)) != 0 editing = self._editIndex == modelIndex self.document.setHtml(text) self.document.setTextWidth(self._preferredMessageWidth(option.rect.width())) ctx = QAbstractTextDocumentLayout.PaintContext() # Highlighting text if item is selected if selected: ctx.palette.setColor(QPalette.Text, option.palette.color(QPalette.Active, QPalette.HighlightedText)) # total rect for us to paint in textRect = option.rect # final rect to paint message in messageRect = self._getMessageRect(option, self.document, modelIndex) painter.save() mouseOver = (int(option.state) & int(QStyle.State_MouseOver)) != 0 if mouseOver: self.mouseOverDocument = QTextDocument() self.mouseOverDocument.setHtml(text) self.mouseOverDocument.setTextWidth(self._preferredMessageWidth(option.rect.width())) self.mouseOverDocumentRow = modelIndex.row() self.lastTextPos = textRect.topLeft() self.mouseOverOption = option # draw decoration painter.translate(textRect.topLeft()) statusIcon = modelIndex.data(ChatMessagesModel.STATUS_ICON_ROLE) if statusIcon != None and not statusIcon.isNull(): statusIcon = QIcon(statusIcon) if rightAligned: statusIcon.paint(painter, textRect.size().width() - 19, 8, 16, 16, Qt.AlignCenter) else: statusIcon.paint(painter, 3, 8, 16, 16, Qt.AlignCenter) # draw message painter.restore() painter.save() painter.setRenderHint(QPainter.Antialiasing) painter.translate(messageRect.topLeft()) if not editing: painter.setBrush(self._ownBrush if rightAligned else self._otherBrush) painter.setPen(self._ownPenColor if rightAligned else self._otherPenColor) painter.drawRoundedRect(QRectF(QPointF(0, 0.5), QSizeF(self.document.idealWidth(), self.document.size().height() - 1.)), 7, 7) painter.setClipRect(textRect.translated(-textRect.topLeft())) if not editing: self.document.documentLayout().draw(painter, ctx) painter.restore() startEditing = pyqtSignal(QModelIndex) def shouldStartEditAt(self, eventPos, modelIndex): option = QStyleOptionViewItemV4() option.initFrom(self.parent()) option.rect.setHeight(32) self.initStyleOption(option, modelIndex) if modelIndex.row() != self.mouseOverDocumentRow: # TODO reset document self.logger.warning("shouldStartEditAt(): wrong mouse over document") return False messageRect = self._getMessageRect(self.mouseOverOption, self.mouseOverDocument, modelIndex) anchorPos = QPointF(eventPos) - QPointF(messageRect.topLeft()) anchor = self.mouseOverDocument.documentLayout().anchorAt(anchorPos) if anchor != "": return False return messageRect.contains(eventPos) def editorEvent(self, event, _model, option_, modelIndex): if self._column and modelIndex.column() != self._column: return False option = QStyleOptionViewItemV4(option_) self.initStyleOption(option, modelIndex) text = QString(option.text) if not text: self.parent().unsetCursor() return False if event.type() not in (QEvent.MouseMove, QEvent.MouseButtonRelease, QEvent.MouseButtonPress) \ or not (option.state & QStyle.State_Enabled): return False if modelIndex.row() != self.mouseOverDocumentRow: return False # Get the link at the mouse position pos = event.pos() messageRect = self._getMessageRect(option, self.mouseOverDocument, modelIndex) anchor = convert_string(self.mouseOverDocument.documentLayout().anchorAt(QPointF(pos) - QPointF(messageRect.topLeft()))) if anchor == "": if messageRect.contains(pos): self.parent().setCursor(Qt.IBeamCursor) else: self.parent().unsetCursor() else: self.parent().setCursor(Qt.PointingHandCursor) if event.type() == QEvent.MouseButtonRelease: if anchor.startswith(u"www."): anchor = u"http://" + anchor webbrowser.open(anchor) return True return False def sizeHint(self, option1, index): # option.rect is a zero rect width = self.parent().columnWidth(index.column()) if index.data(Qt.DisplayRole).type() == QMetaType.Double: return QSize(width, 18) option = QStyleOptionViewItemV4(option1) self.initStyleOption(option, index) if not option.text: iconSize = option.icon.actualSize(QSize(32, 32)) return QSize(32, max(32, iconSize.height() + 4)) doc = QTextDocument() doc.setHtml(option.text) doc.setTextWidth(self._preferredMessageWidth(width)) return QSize(doc.idealWidth(), max(32, doc.size().height() + 4))