gpt4 book ai didi

c++ - 将鼠标悬停在 QListWidget 项目上时如何绘制轮廓?

转载 作者:行者123 更新时间:2023-12-03 07:15:34 24 4
gpt4 key购买 nike

当鼠标悬停在 QListWidget 项目上时,我试图在该项目周围绘制轮廓。我对 QStyledItemDelegate 进行了子类化并覆盖 paintQStyle::State_MouseOver情况如下:

class MyDelegate : public QStyledItemDelegate
{
Q_OBJECT

public:

MyDelegate(QObject *parent = nullptr)
: QStyledItemDelegate(parent){}

void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override
{
QStyledItemDelegate::paint(painter, option, index);
if(option.state & QStyle::State_MouseOver) painter->drawRect(option.rect);
}

~MyDelegate(){}
};
然后我用一些项目实例化一个 QListWidget 并启用 Qt::WA_Hover属性:
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QListWidget w;
w.addItems(QStringList{"item1", "item2", "item3", "item4"});
w.setItemDelegate(new MyDelegate(&w));
w.viewport()->setAttribute(Qt::WA_Hover);
w.show();
return a.exec();
}
不幸的是,这种行为不是我所期望的。特别是,当我将鼠标移到一个项目上时,会绘制轮廓,但是当我移动到另一个项目时,第一个项目周围的轮廓不会被删除。相反,它会不断在我将鼠标移到的所有项目周围绘制轮廓,最终在所有项目周围都有一个轮廓。这是正常的吗?我知道另一种解决方案是使用 QStyleSheet s 但我想了解为什么当前的方法不像我预期的那样运行。
这是鼠标悬停之前小部件的外观:
enter image description here
这是在将鼠标悬停在 item2 上之后:
enter image description here
然后在item3之后:
enter image description here
我在 MacOS 10.15.6 平台上使用 Qt 5.15.1。
编辑 1:
根据 scopchanov 的回答,为确保轮廓厚度确实为 1px,我更改了 paint方法:
void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override
{
int outlineWidth = 1;
QPen pen;
pen.setWidth(outlineWidth);
painter->setPen(pen);

QStyledItemDelegate::paint(painter, option, index);
if(option.state & QStyle::State_MouseOver) {

int a = round(0.5*(outlineWidth - 1));
int b = round(-0.5*outlineWidth);

painter->drawRect(option.rect.adjusted(a, a, b, b));
}
}
不幸的是,行为非常相似。这是从上到下悬停在所有项目上后的屏幕截图:
enter image description here

最佳答案

原因QPainter::drawRect绘制一个比绘制区域略大(高度和宽度正好一个像素)的矩形。这种行为的原因可以在 the way QPaintEngine draws a rectangle 中看到。 :

for (int i=0; i<rectCount; ++i) {
QRectF rf = rects[i];
QPointF pts[4] = { QPointF(rf.x(), rf.y()),
QPointF(rf.x() + rf.width(), rf.y()),
QPointF(rf.x() + rf.width(), rf.y() + rf.height()),
QPointF(rf.x(), rf.y() + rf.height()) };
drawPolygon(pts, 4, ConvexMode);
}
QPaintEngine绘制一个封闭的多边形,从点 (x, y) 开始, 转至 (x + width, y) ,然后转至 (x + width, y + height)最后到 (x, y + height) .这看起来很直观,但是让我们看看如果我们用实数替换这些变量会发生什么:
比如说,我们要画一个 4x2 (0, 0) 处的 px 矩形. QPaintEngine将使用以下坐标: (0, 0) , (4, 0) , (4, 2)(0, 2) .以像素表示,绘图将如下所示:
Pixel zoomed rectangle
所以,而不是 4x2 px,我们最终得到 5x3 px 矩形,即确实宽和高一个像素。
您可以通过将画家剪裁为 option.rect 来进一步证明这一点。在调用 drawRect 之前像这样:
if (option.state & QStyle::State_MouseOver) {
painter->setClipRect(option.rect);
painter->drawRect(option.rect);
}
结果是轮廓的底部和右侧边缘被剪裁(非常边缘,我们预计在绘制区域内):
Clipped outline
在任何情况下,位于绘制区域之外的轮廓部分都没有正确重新绘制,因此之前的图形以线条的形式留下了不需要的残留物。
解决方案
使用 QRect::adjusted 减小轮廓的高度和宽度.
你可能只是写
painter->drawRect(option.rect.adjusted(0, 0, -1, -1));
但是,这仅适用于轮廓,即 1px 厚和 devicePixelRatio是 1,就像在 PC 上一样。如果轮廓的边框大于 1px 和/或 devicePixelRatio是 2,就像在 Mac 上一样,当然更多的轮廓会伸出绘制区域,所以你应该考虑到这一点并相应地调整矩形,例如:
int effectiveOutlineWidth = m_outineWidth*m_devicePixelRatio;
int tl = round(0.5*(effectiveOutlineWidth - 1));
int br = round(-0.5*effectiveOutlineWidth);

painter->drawRect(option.rect.adjusted(tl, tl, br, br));
m_outineWidthm_devicePixelRatio是类成员,分别代表所需的轮廓宽度。绘制设备的物理像素和与设备无关的像素之间的比率。如果你已经为它们创建了公共(public)的 setter 方法,你可以像这样设置它们的值:
auto *delegate = new MyDelegate(&w);

delegate->setOutlineWidth(1);
delegate->setDevicePixelRatio(w.devicePixelRatio());

w.setItemDelegate(delegate);
例子
这是我为您编写的示例,用于演示如何实现建议的解决方案:
#include <QApplication>
#include <QStyledItemDelegate>
#include <QListWidget>
#include <QPainter>

class MyDelegate : public QStyledItemDelegate
{
int m_outineWidth;
int m_devicePixelRatio;
public:

MyDelegate(QObject *parent = nullptr) :
QStyledItemDelegate(parent),
m_outineWidth(1),
m_devicePixelRatio(1) {
}

void paint(QPainter *painter, const QStyleOptionViewItem &option,
const QModelIndex &index) const override {
QStyledItemDelegate::paint(painter, option, index);

if (option.state & QStyle::State_MouseOver) {
int effectiveOutlineWidth = m_outineWidth*m_devicePixelRatio;
int tl = round(0.5*(effectiveOutlineWidth - 1));
int br = round(-0.5*effectiveOutlineWidth);

painter->setPen(QPen(QBrush(Qt::red), m_outineWidth, Qt::SolidLine,
Qt::SquareCap, Qt::MiterJoin));
painter->drawRect(option.rect.adjusted(tl, tl, br, br));
}
}

void setOutlineWidth(int outineWidth) {
m_outineWidth = outineWidth;
}

void setDevicePixelRatio(int devicePixelRatio) {
m_devicePixelRatio = devicePixelRatio;
}
};

int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QListWidget w;
auto *delegate = new MyDelegate(&w);

delegate->setOutlineWidth(3);
delegate->setDevicePixelRatio(w.devicePixelRatio());

w.setItemDelegate(delegate);
w.addItems(QStringList{"item1", "item2", "item3", "item4"});
w.viewport()->setAttribute(Qt::WA_Hover);
w.show();

return a.exec();
}
结果
提供的示例在 Windows 上为 3px 粗轮廓生成以下结果:
3px thick outline

关于c++ - 将鼠标悬停在 QListWidget 项目上时如何绘制轮廓?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/64576846/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com