Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/qt/6.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python 不带QLabel的QTreeView中的超链接_Python_Qt_Hyperlink_Pyqt_Qtreeview - Fatal编程技术网

Python 不带QLabel的QTreeView中的超链接

Python 不带QLabel的QTreeView中的超链接,python,qt,hyperlink,pyqt,qtreeview,Python,Qt,Hyperlink,Pyqt,Qtreeview,我正在尝试在我的QTreeView中显示可单击的超链接 根据这个问题的建议,我可以使用QLabels和QTreeView.setIndexWidget来实现这一点 不幸的是,我的QTreeView可能相当大(1000个条目),创建1000个qlabel的速度很慢 好处是我可以在QTreeView中使用委托来绘制看起来像超链接的文本。这太快了 现在的问题是,我需要它们像超链接一样响应(即鼠标越过鼠标指针,响应点击等),但我不确定最好的方式是什么 我可以通过连接到QTreeView的clicked

我正在尝试在我的QTreeView中显示可单击的超链接

根据这个问题的建议,我可以使用QLabels和QTreeView.setIndexWidget来实现这一点

不幸的是,我的QTreeView可能相当大(1000个条目),创建1000个qlabel的速度很慢

好处是我可以在QTreeView中使用委托来绘制看起来像超链接的文本。这太快了

现在的问题是,我需要它们像超链接一样响应(即鼠标越过鼠标指针,响应点击等),但我不确定最好的方式是什么


我可以通过连接到QTreeView的clicked()信号来伪造它,但它并不完全相同,因为它响应整个单元格,而不仅仅是单元格内的文本。

可能可以避免使用qlabel,但它可能会影响代码的可读性


可能不需要一次填满整棵树。您是否考虑过根据需要生成QLabel?分配足够的资源,用expand和expandAll信号覆盖一个子树。您可以通过创建Qlabel池并根据需要更改它们的文本(以及它们的使用位置)来扩展此功能。

最简单的方法似乎是通过子类化
QItemDelegate
,因为文本是由一个单独的虚拟函数绘制的,
drawDisplay
(使用
QStyledItemDelegate
几乎必须从头开始重新绘制项目,并且需要从
QProxyStyle
派生的附加类):

  • HTML文本是用
    QTextDocument
    QTextDocument.documentLayout().draw()绘制的
  • 当鼠标输入一个项目时,将重新绘制该项目,并调用
    drawDisplay
    ,我们保存绘制文本时的位置(因此保存的位置始终是鼠标所在项目的文本位置)
  • 该位置在
    editorEvent
    中使用,以获取鼠标在文档中的相对位置,并使用
    QAbstractTextDocumentLayout.anchorAt
    获取文档中该位置的链接
导入系统 从PySide.QtCore导入* 从PySide.QtGui导入* 类LinkItemDelegate(QItemDelegate): 链路激活=信号(str) linkHovered=Signal(str)#连接到QStatusBar.showMessage插槽 定义初始化(self,parentView): QItemDelegate.\uuuu init\uuuu(self,parentView) 断言isinstance(parentView、QAbstractItemView)\ “第一个参数必须是视图” #我们需要它来接收editorEvent中的鼠标移动事件 parentView.setMouseTracking(True) #当鼠标未在上方时,还原鼠标光标 #一个项目,但仍在视图小部件上 parentView.viewportEntered.connect(parentView.unsetCursor) #文档[0]将包含最后一个悬停项的文档 #文档[1]将用于绘制普通(非悬停)项目 self.documents=[] 对于范围(2)中的i: self.documents.append(QTextDocument(self)) self.documents[i].setDocumentMargin(0) self.lastTextPos=QPoint(0,0) def drawDisplay(自绘制、绘制、选项、矩形、文本): #因为只有当鼠标在行上时,状态才会显示 #我们得检查一下是否也在这个项目上 mouseOver=option.state&QStyle.state\u mouseOver\ 和rect.contains(self.parent().viewport())\ .mapFromGlobal(QCursor.pos())\ 和option.state&QStyle.state_已启用 如果鼠标悬停: #使用文档[0]并保存editorEvent的文本位置 doc=self.documents[0] self.lastTextPos=rect.topLeft() doc.setDefaultStyleSheet(“”) 其他: doc=self.documents[1] #默认情况下,链接是修饰的,因此请禁用它 #当鼠标不在项目上方时 setDefaultStyleSheet(“a{text-decoration:none}”) doc.setDefaultFont(option.font) doc.setHtml(文本) 保存 painter.translate(rect.topLeft()) ctx=QAbstractTextDocumentLayout.PaintContext() ctx.palete=option.palete doc.documentLayout().draw(画师,ctx) 恢复 def editorEvent(自身、事件、模型、选项、索引): 如果event.type()不在[QEvent.MouseMove,QEvent.MouseButtonRelease]中\ 或不启用(option.state&QStyle.state_已启用): 返回错误 #在鼠标位置获取链接 #(仅PyQt需要显式QPointF转换) pos=QPointF(event.pos()-self.lastTextPos) anchor=self.documents[0].documentLayout().anchorAt(位置) 如果锚点==“”: self.parent() 其他: self.parent().setCursor(Qt.PointingHandCursor) 如果event.type()==QEvent.MouseButtonRelease: self.linkActivated.emit(锚定) 返回真值 其他: self.linkHovered.emit(锚定) 返回错误 def sizeHint(自身、选项、索引): #原始大小是从带有html标记的字符串计算出来的 #所以我们需要从中减去宽度之间的差值 #包含和不包含html标记的文本的 size=QItemDelegate.sizeHint(self、option、index) #使用QTextDocument剥离标记 doc=self.documents[1] html=index.data()#必须为PyQt“API 1”添加.toString() doc.setHtml(html) 明文=doc.toPlainText() fontMetrics=QFontMetrics(option.font) diff=fontMetrics.width(html)-fontMetrics.width(纯文本) 返回大小-QSize(差异,0) 只要你不启用
class LinkItemDelegate(QStyledItemDelegate):
linkActivated = pyqtSignal(str)
linkHovered = pyqtSignal(str)  # to connect to a QStatusBar.showMessage slot

def __init__(self, parentView):
    super(LinkItemDelegate, self).__init__(parentView)
    assert isinstance(parentView, QAbstractItemView), \
        "The first argument must be the view"

    # 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(parentView.unsetCursor)

    # documents[0] will contain the document for the last hovered item
    # documents[1] will be used to draw ordinary (not hovered) items
    self.documents = []
    for i in range(2):
        self.documents.append(QTextDocument(self))
        self.documents[i].setDocumentMargin(0)
    self.lastTextPos = QPoint(0,0)

def drawDisplay(self, painter, option, rect, text): 
    # Because the state tells only if the mouse is over the row
    # we have to check if it is over the item too
    mouseOver = option.state & QStyle.State_MouseOver \
        and rect.contains(self.parent().viewport() \
            .mapFromGlobal(QCursor.pos())) \
        and option.state & QStyle.State_Enabled

    # Force to be vertically align
    fontMetrics = QFontMetrics(option.font)
    rect.moveTop(rect.y() + rect.height() / 2 - fontMetrics.height() / 2)

    if mouseOver:
        # Use documents[0] and save the text position for editorEvent
        doc = self.documents[0]
        self.lastTextPos = rect.topLeft()
        doc.setDefaultStyleSheet("")
    else:
        doc = self.documents[1]
        # Links are decorated by default, so disable it
        # when the mouse is not over the item
        doc.setDefaultStyleSheet("a {text-decoration: none; }")

    doc.setDefaultFont(option.font)
    doc.setHtml(text)

    painter.save()
    painter.translate(rect.topLeft())
    ctx = QAbstractTextDocumentLayout.PaintContext()
    ctx.palette = option.palette
    doc.documentLayout().draw(painter, ctx)
    painter.restore()

def editorEvent(self, event, model, option, index):
    if event.type() not in [QEvent.MouseMove, QEvent.MouseButtonRelease] \
        or not (option.state & QStyle.State_Enabled):
        return False
    # Get the link at the mouse position
    # (the explicit QPointF conversion is only needed for PyQt)
    pos = QPointF(event.pos() - self.lastTextPos)
    anchor = self.documents[0].documentLayout().anchorAt(pos)
    if anchor == "":
        self.parent().unsetCursor()
    else:
        self.parent().setCursor(Qt.PointingHandCursor)
        if event.type() == QEvent.MouseButtonRelease:
            self.linkActivated.emit(anchor)
            return True 
        else:
            self.linkHovered.emit(anchor)
    return False

def sizeHint(self, option, index):
    # The original size is calculated from the string with the html tags
    # so we need to subtract from it the difference between the width
    # of the text with and without the html tags
    size = super(LinkItemDelegate, self).sizeHint(option, index)
    if option.text.startswith('<a'):
        # Use a QTextDocument to strip the tags
        doc = self.documents[1]
        html = index.data() # must add .toString() for PyQt "API 1"
        doc.setHtml(html)
        plainText = doc.toPlainText()

        fontMetrics = QFontMetrics(option.font)
        diff = fontMetrics.width(html) - fontMetrics.width(plainText)
        size = size - QSize(diff, 0)

    return size

def paint(self, painter, option, index):
    if (index.isValid()):
        text = None
        options = QStyleOptionViewItem(option)
        self.initStyleOption(options,index)
        if options.text.startswith('<a'):
            text = options.text
            options.text = ""
        style = options.widget.style() if options.widget.style() else QApplication.style()
        style.drawControl(QStyle.CE_ItemViewItem, options, painter, options.widget)
        if text:
            textRect = style.subElementRect(QStyle.SE_ItemViewItemText, options, options.widget)
            self.drawDisplay(painter, option, textRect, text)
linkItemDelegate = LinkItemDelegate(self.my_treeView)
linkItemDelegate.linkActivated.connect(self.onClicLink)
self.my_treeView.setItemDelegate(linkItemDelegate) # Create custom delegate and set model and delegate to the treeview object