- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我有一个 QTreeWidget,我想通过使用样式委托(delegate)来完全自定义项目的外观。
我的主要问题是我想在我的项目右侧创建一个自定义按钮,它允许我折叠和展开该项目的子项。经典的“+”按钮通常可以在大多数树的左侧找到。
我可以毫无问题地绘制按钮本身,并根据项目是否展开来更改其图标。问题是让它表现得像一个按钮(按下时激活命令,悬停时改变颜色等......)
我想到的是使用 editorEvent 来检查鼠标是否按下在与我绘制当前项目的按钮相同的位置上。
为了获得悬停效果,我编辑了树的 mouseMoveEvent 并检查鼠标是否位于该项目的按钮顶部,如果是,则在悬停时重新绘制该项目。
我的实现完成了这项工作,但我担心我做得完全错误,效率不高,而且我的树会因为这种计算而变慢。所以我想知道是否有人对如何改进下面的代码有一些建议。
代表
class styleDelegate(QtWidgets.QStyledItemDelegate):
def __init__(self, parent=None, treeWidget = None):
super(styleDelegate, self).__init__(parent)
self.tree = treeWidget
def paint(self, painter, option, index):
painter.save()
rect = option.rect
# set the pen to draw an outline around the item to divide them.
pen = QPen()
pen.setBrush(QtGui.QColor(43, 43, 43))
pen.setWidthF(1)
painter.setPen(pen)
item = self.tree.itemFromIndex(index)
# set the background color based on the item or if it is selected
if option.state & QStyle.State_Selected:
painter.setBrush(option.palette.highlight())
else:
color = item.color
painter.setBrush(QtGui.QColor(color[0] * 255, color[1] * 255, color[2] * 255))
#draw the colored background
painter.drawRect(rect)
#draw the image
imageScale = 0
margin = 4
imageScale = rect.height() - margin * 2 + 1
painter.drawPixmap(rect.x() + margin, rect.y() + margin , imageScale, imageScale, item.image.scaled(imageScale, imageScale, QtCore.Qt.KeepAspectRatio, QtCore.Qt.SmoothTransformation))
# draw the text
painter.setPen(QtGui.QColor(255, 255, 255))
font = painter.font()
font.setPointSize(9)
painter.setFont(font)
painter.drawText(rect.x() + imageScale + margin * 3, rect.y(), 100, item.scale, Qt.AlignVCenter, item.name)
# draw the expander button only if the item has children
if item.childCount():
# choose the appropriate icon to draw depending on the state of the item.
if item.isExpanded():
path = "checked.png"
if item.hover:
path = "checked_hover.png"
else:
path = "unchecked.png"
if item.hover:
path = "unchecked_hover.png"
image = QtGui.QPixmap.fromImage(QtGui.QImage(path))
size = 20
# define the position of the expander button
positionX = rect.x() + rect.width() - 20
positionY = rect.y() + item.scale / 2 - size/2
painter.drawPixmap(positionX, positionY, size, size, image.scaled(size, size, QtCore.Qt.KeepAspectRatio, QtCore.Qt.SmoothTransformation))
item.expanderStart = QPoint(positionX, positionY)
item.expanderEnd = QPoint(positionX + 20, positionY + 20)
painter.restore()
def editorEvent(self, event, model, option, index):
# if an item is clicked, check if the click happened in the area whee the expander button is drawn.
if event.type() == QEvent.MouseButtonPress:
item = self.tree.itemFromIndex(index)
rect = option.rect
clickX = event.x()
clickY = event.y()
# set the expanded expanded if it was clicked
if clickX > x and clickX < x + w:
if clickY > y and clickY < y + h:
item.setExpanded(not item.isExpanded())
树
class myTree(QtWidgets.QTreeWidget):
def __init__(self, parent):
super(myTree, self).__init__(parent)
self.setMouseTracking(True)
def mouseMoveEvent(self, event)
item = self.itemAt(event.pos())
if item:
if item.childCount():
# get the current hovering state. if the item is already hovered, there is no need to repaint it.
hover = item.hover
if (event.pos.x() > item.expanderStart.x()
and event.pos.x() < item.expanderEnd.x()
and event.pos.y() > item.expanderStart.y()
and event.pos.y() < item.expanderEnd.y())
item.hover = True
else:
item.hover = False
if item.hover != hover:
self.viewport().update(event.pos().x(), event.pos().y(), 20, 20)
我知道这可以在不使用委托(delegate)的情况下完全实现,只需使用样式表或将小部件分配给项目即可。然而,我并没有深入研究这些方法,因为我遇到了一些问题。
我花了很多时间试图达到我想要的结果但没有成功。也许我让我的元素看起来接近我想要的,但从来没有完全像我想象的那样。
我之所以如此挑剔地想要得到我想要的委托(delegate)外观,是因为这个 QTreeWidget 曾经是一个 QListWidget,是用样式表实现的。现在我将其“升级”为一棵树,我什至不希望用户注意到差异,但我无法仅使用样式表复制相同的外观。
如果上面的代码有愚蠢的错误,请原谅我,我已经测试了完整版本并且它可以工作,我刚刚在这里发布了相关的内容。
<小时/>编辑:
根据要求,这是一些代码(至少对我来说)产生所需的结果。但是,我想知道这是否是我正在做的事情的正确方法......
from PySide2.QtGui import *
from PySide2.QtCore import *
from PySide2.QtWidgets import *
class styleDelegate(QStyledItemDelegate):
def __init__(self, parent=None, treeWidget = None):
super(styleDelegate, self).__init__(parent)
self.tree = treeWidget
def paint(self, painter, option, index):
painter.save()
rect = option.rect
# set the pen to draw an outline around the item to divide them.
pen = QPen()
pen.setBrush(QColor(43, 43, 43))
pen.setWidthF(1)
painter.setPen(pen)
item = self.tree.itemFromIndex(index)
# set the background color based on the item or if it is selected
if option.state & QStyle.State_Selected:
painter.setBrush(option.palette.highlight())
else:
color = item.color
painter.setBrush(QColor(color[0], color[1], color[2]))
#draw the colored background
painter.drawRect(rect)
#draw the image
margin = 4
imageScale = rect.height() - margin * 2 + 1
painter.drawPixmap(rect.x() + margin, rect.y() + margin , imageScale, imageScale, item.image.scaled(imageScale, imageScale, Qt.KeepAspectRatio, Qt.SmoothTransformation))
# draw the text
painter.setPen(QColor(255, 255, 255))
font = painter.font()
font.setPointSize(9)
painter.setFont(font)
painter.drawText(rect.x() + imageScale + margin * 3, rect.y(), 300, item.scale, Qt.AlignLeft|Qt.AlignVCenter, item.name)
# draw the expander button only if the item has children
if item.childCount():
# choose the appropriate icon to draw depending on the state of the item.
if item.isExpanded():
path = "c:\\test.png"
if item.hover:
path = "c:\\test.png"
else:
path = "c:\\test.png"
if item.hover:
path = "c:\\test.png"
image = QPixmap.fromImage(QImage(path))
size = self.tree.expanderSize
# define the position of the expander button
positionX = rect.x() + rect.width() - size - 10
positionY = rect.y() + item.scale / 2 - size/2
painter.drawPixmap(positionX, positionY, size, size, image.scaled(size, size, Qt.KeepAspectRatio, Qt.SmoothTransformation))
item.expanderStart = QPoint(positionX, positionY)
item.expanderEnd = QPoint(positionX + size, positionY + size)
painter.restore()
def editorEvent(self, event, model, option, index):
# if an item is clicked, check if the click happened in the area whee the expander button is drawn.
if event.type() == QEvent.MouseButtonPress:
item = self.tree.itemFromIndex(index)
if item.childCount():
rect = option.rect
clickX = event.x()
clickY = event.y()
size = self.tree.expanderSize
# this is the rect of the expander button
x = rect.x() + rect.width() - 20
y = rect.y() + item.scale / 2 - size/2
w = size # expander width
h = size # expander height
# set the expanded expanded if it was clicked
if (clickX > item.expanderStart.x()
and clickX < item.expanderEnd.x()
and clickY > item.expanderStart.y()
and clickY < item.expanderEnd.y()):
print "expand"
item.setExpanded(not item.isExpanded())
class myTree(QTreeWidget):
def __init__(self, parent = None):
super(myTree, self).__init__(parent)
self.setMouseTracking(True)
self.setHeaderHidden(True)
self.setRootIsDecorated(False)
self.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
def mouseMoveEvent(self, event):
item = self.itemAt(event.pos())
if item:
if item.childCount():
# get the current hovering state. if the item is already hovered, there is no need to repaint it.
hover = item.hover
if (event.pos() .x() > item.expanderStart.x()
and event.pos() .x() < item.expanderEnd.x()
and event.pos() .y() > item.expanderStart.y()
and event.pos() .y() < item.expanderEnd.y()):
item.hover = True
else:
item.hover = False
if item.hover != hover:
self.viewport().update(event.pos().x(), event.pos().y(), 20, 20)
print "Hover", item.hover
def closeEvent(self, event):
self.deleteLater()
def generateTree():
tree = myTree()
tree.setGeometry(500, 500, 1000, 500)
tree.expanderSize = 50
delegate = styleDelegate(tree, treeWidget = tree)
tree.setItemDelegate(delegate)
for object in ["Aaaaaaa", "Bbbbbbb", "Ccccccc"]:
item = QTreeWidgetItem()
item.name = object
item.image = QPixmap.fromImage(QImage("c:\\test.png"))
item.color = [150, 150, 150]
item.hover = False
item.scale = 100
tree.addTopLevelItem(item)
item.setSizeHint(0, QSize(item.scale, item.scale ))
for child in ["Eeeeee", "Fffffff"]:
childItem = QTreeWidgetItem()
childItem.name = child
childItem.image = QPixmap.fromImage(QImage("c:\\test.png"))
childItem.color = [150, 150, 150]
childItem.scale = 90
item.addChild(childItem)
childItem.setSizeHint(0, QSize(childItem.scale, childItem.scale))
return tree
tree = generateTree()
tree.show()
请注意,我的显示器是 4k,并且我很快对大部分尺寸进行了硬编码,因此开箱即用的此代码将在高清显示器上生成更大的小部件。
最佳答案
您的代码有以下错误:
不一定要使用QPixmap.fromImage(QImage(path))
,可以直接用路径创建QPixmap
:QPixmap (路径)
如果它们是相同的图像,最好加载一次并重复使用它,例如在我的解决方案中,我为按钮 QPixmap 执行此操作。
不要创建动态属性,因为它会生成代码耦合,对于项目,您必须使用角色。
要知道某个项目是否已展开,或者您不应该使用QStyle::State_Open
,这样我们就可以避免耦合,并且委托(delegate)可以由其他 View 使用,而无需进行太多更改。
使用 QRect
来界定矩形,例如,您可以使用 contains 来查看某个点是否在矩形内。
以上是主要的观察结果,下面是解决方案:
from PySide2 import QtCore, QtGui, QtWidgets
from enum import Enum
ScaleRole= QtCore.Qt.UserRole + 1
expanderSize = 50
class TreeDelegate(QtWidgets.QStyledItemDelegate):
def __init__(self, parent=None):
super(TreeDelegate, self).__init__(parent)
self.pixmap_collapsed = QtGui.QPixmap("collapsed.png")
self.pixmap_collapsed_hover = QtGui.QPixmap("collapsed_hover.png")
self.pixmap_expanded = QtGui.QPixmap("expanded.png")
self.pixmap_expanded_hover = QtGui.QPixmap("expanded_hover.png")
def paint(self, painter, option, index):
image = index.data(QtCore.Qt.DecorationRole)
scale = index.data(ScaleRole)
name = index.data()
painter.save()
rect = option.rect
painter.setPen(QtGui.QPen(brush=QtGui.QColor(43, 43, 43), widthF=1))
if option.state & QtWidgets.QStyle.State_Selected:
painter.setBrush(option.palette.highlight())
else:
painter.setBrush(index.data(QtCore.Qt.BackgroundRole))
painter.drawRect(rect)
margin = 4
image_scale = (rect.height() - margin * 2 + 1)*QtCore.QSize(1, 1)
if image is not None and not image.isNull():
image = image.scaled(image_scale, QtCore.Qt.KeepAspectRatio, QtCore.Qt.SmoothTransformation)
painter.drawPixmap(rect.topLeft() + margin*QtCore.QPoint(1, 1), image)
painter.setPen(QtGui.QColor(255, 255, 255))
font = painter.font()
font.setPointSize(9)
painter.setFont(font)
painter.drawText(QtCore.QRect(rect.topLeft() + QtCore.QPoint(image_scale.width() + 3*margin, 0) , QtCore.QSize(300, scale)),
QtCore.Qt.AlignLeft | QtCore.Qt.AlignVCenter, name)
if index.model().hasChildren(index):
pixmap = self.pixmap_collapsed
if option.state & QtWidgets.QStyle.State_Open:
if option.state & QtWidgets.QStyle.State_MouseOver:
pixmap = self.pixmap_expanded_hover
else:
pixmap = self.pixmap_expanded
else :
if option.state & QtWidgets.QStyle.State_MouseOver:
pixmap = self.pixmap_collapsed_hover
size = expanderSize
pixmap = pixmap.scaled(size*QtCore.QSize(1, 1), QtCore.Qt.KeepAspectRatio, QtCore.Qt.SmoothTransformation)
pos = rect.topRight() - QtCore.QPoint(size+10, (size-scale)/2)
painter.drawPixmap(pos, pixmap)
painter.restore()
class MyTreeItem(QtWidgets.QTreeWidgetItem):
def __init__(self, name, image, color, scale):
super(MyTreeItem, self).__init__([name])
self.setData(0, ScaleRole, scale)
self.setData(0, QtCore.Qt.BackgroundRole, color)
self.setData(0, QtCore.Qt.DecorationRole, image)
class MyTree(QtWidgets.QTreeWidget):
def __init__(self, parent=None):
super(MyTree, self).__init__(parent)
self.setMouseTracking(True)
self.setHeaderHidden(True)
self.setRootIsDecorated(False)
self.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
def mousePressEvent(self, event):
if not self.itemsExpandable(): return
index = self.indexAt(event.pos())
if not index.isValid(): return
# restore state
is_expanded = self.isExpanded(index)
QtWidgets.QAbstractItemView.mousePressEvent(self, event)
self.setExpanded(index, is_expanded)
if not self.model().hasChildren(index): return
rect = self.visualRect(index)
size = expanderSize
scale = index.data(ScaleRole)
pos = rect.topRight() - QtCore.QPoint(size+10, (size-scale)/2)
r = QtCore.QRect(pos, size*QtCore.QSize(1, 1))
if r.contains(event.pos()):
self.setExpanded(index, not self.isExpanded(index))
def generate_tree():
tree = MyTree()
scale = 100
delegate = TreeDelegate(tree)
tree.setItemDelegate(delegate)
for text in ["Aaaaaaa", "Bbbbbbb", "Ccccccc"]:
item = MyTreeItem(text, QtGui.QPixmap("image.png"), QtGui.QColor(150, 150, 150), scale)
item.setSizeHint(0, QtCore.QSize(scale, scale))
tree.addTopLevelItem(item)
for child in ["Eeeeee", "Fffffff"]:
childItem = MyTreeItem(child, QtGui.QPixmap("image.png"), QtGui.QColor(150, 150, 150), scale)
childItem.setSizeHint(0, QtCore.QSize(scale, scale))
item.addChild(childItem)
return tree
if __name__ == '__main__':
import sys
app = QtWidgets.QApplication(sys.argv)
tree = generate_tree()
tree.show()
sys.exit(app.exec_())
关于python - 带有自定义按钮的 QStyledItemDelegate,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52825132/
好的,所以我编辑了以下... 只需将以下内容放入我的 custom.css #rt-utility .rt-block {CODE HERE} 但是当我尝试改变... 与 #rt-sideslid
在表格 View 中,我有一个自定义单元格(在界面生成器中高度为 500)。在该单元格中,我有一个 Collection View ,我按 (10,10,10,10) 固定到边缘。但是在 tablev
对于我的无能,我很抱歉,但总的来说,我对 Cocoa、Swift 和面向对象编程还很陌生。我的主要来源是《Cocoa Programming for OS X》(第 5 版),以及 Apple 的充满
我正在使用 meta-tegra 为我的 NVIDIA Jetson Nano 构建自定义图像。我需要 PyTorch,但没有它的配方。我在设备上构建了 PyTorch,并将其打包到设备上的轮子中。现
在 jquery 中使用 $.POST 和 $.GET 时,有没有办法将自定义变量添加到 URL 并发送它们?我尝试了以下方法: $.ajax({type:"POST", url:"file.php?
Traefik 已经默认实现了很多中间件,可以满足大部分我们日常的需求,但是在实际工作中,用户仍然还是有自定义中间件的需求,为解决这个问题,官方推出了一个 Traefik Pilot[1] 的功
我想让我的 CustomTextInputLayout 将 Widget.MaterialComponents.TextInputLayout.OutlinedBox 作为默认样式,无需在 XML 中
我在 ~/.emacs 中有以下自定义函数: (defun xi-rgrep (term) (grep-compute-defaults) (interactive "sSearch Te
我有下表: 考虑到每个月的权重,我的目标是在 5 个月内分散 10,000 个单位。与 10,000 相邻的行是我最好的尝试(我在这上面花了几个小时)。黄色是我所追求的。 我试图用来计算的逻辑如下:计
我的表单中有一个字段,它是文件类型。当用户点击保存图标时,我想自然地将文件上传到服务器并将文件名保存在数据库中。我尝试通过回显文件名来测试它,但它似乎不起作用。另外,如何将文件名添加到数据库中?是在模
我有一个 python 脚本来发送电子邮件,它工作得很好,但问题是当我检查我的电子邮件收件箱时。 我希望该用户名是自定义用户名,而不是整个电子邮件地址。 最佳答案 发件人地址应该使用的格式是: You
我想减小 ggcorrplot 中标记的大小,并减少文本和绘图之间的空间。 library(ggcorrplot) data(mtcars) corr <- round(cor(mtcars), 1)
GTK+ noob 问题在这里: 是否可以自定义 GtkFileChooserButton 或 GtkFileChooserDialog 以删除“位置”部分(左侧)和顶部的“位置”输入框? 我实际上要
我正在尝试在主页上使用 ajax 在 magento 中使用 ajax 显示流行的产品列表,我可以为 5 或“N”个产品执行此操作,但我想要的是将分页工具栏与结果集一起添加. 这是我添加的以显示流行产
我正在尝试使用 PasswordResetForm 内置函数。 由于我想要自定义表单字段,因此我编写了自己的表单: class FpasswordForm(PasswordResetForm):
据我了解,新的 Angular 7 提供了拖放功能。我搜索了有关 DnD 的 Tree 组件,但没有找到与树相关的内容。 我在 Stackblitz 上找到的一个工作示例.对比drag'ndrop功能
我必须开发一个自定义选项卡控件并决定使用 WPF/XAML 创建它,因为我无论如何都打算学习它。完成后应该是这样的: 到目前为止,我取得了很好的进展,但还有两个问题: 只有第一个/最后一个标签项应该有
我要定制xtable用于导出到 LaTeX。我知道有些问题是关于 xtable在这里,但我找不到我要找的具体东西。 以下是我的表的外观示例: my.table <- data.frame(Specif
用ejs在这里显示日期 它给我结果 Tue Feb 02 2016 16:02:24 GMT+0530 (IST) 但是我需要表现为 19th January, 2016 如何在ejs中执行此操作?
我想问在 JavaFX 中使用自定义对象制作 ListView 的最佳方法,我想要一个每个项目如下所示的列表: 我搜了一下,发现大部分人都是用细胞工厂的方法来做的。有没有其他办法?例如使用客户 fxm
我是一名优秀的程序员,十分优秀!