QTreeView Item Hover/Selected 背景颜色基于当前颜色
QTreeView Item Hover/Selected background color based on current color
在我的项目中,我有几个 QTreeView
显示数据的小部件。 QTreeView
中项目的背景颜色根据数据类型和与其他项目的关联而变化。
这些背景颜色的设置方式如下:
QColor warning;
warning.setRgb(255, 86, 86);
model->itemFromIndex(index)->setData(warning, Qt::BackgroundRole);
这行得通,但是 我还想在项目为 selected/hovered 时使用不同的背景颜色。我选择使用样式表。
QTreeView::item:selected{background-color: #bedcf0;} //light blue
QTreeView::item:hover:selected{background-color: #94c8ea;} //darker blue
QTreeView::item:hover:!selected{background-color: #e6e6e6;} //gray
这提供了我想要的外观,但仅适用于具有白色默认背景的项目。如果项目具有自定义背景颜色(通过 Qt::BackgroundRole
设置),则这些悬停和选定颜色会完全覆盖当前背景颜色。
我想要发生的是在hovered/selected、基于当前背景时让每个项目变暗一个设定的量颜色。这很难,因为 QStandardItem::setProperty()
不存在。
感谢您的宝贵时间!
所以我有一个答案。也许你可以告诉我是否适合你 and/or 我们可以谈谈。
我创建了一个自定义 QTreeView
和 QStandardItem
,覆盖 mouseMoveEvent(QMouseEvent *event)
并设置了我的树 setMouseTracking(true);
。
我通过以下方式获得了鼠标下的项目:static_cast<QStandardItemModel*>(model())->itemFromIndex(indexAt(event->pos()))
这样我就可以得到悬停的项目。然后在我的自定义项目中,我有一个函数 hovered()
和 normal()
。当项目悬停时,将调用 hovered 方法。当鼠标移动时,它会将项目恢复正常并重新悬停它(如果它仍在上面)。
代码:
HoveredTreeView.cpp:
#include "HoverTreeView.h"
#include <QDebug>
#include <QMouseEvent>
#include <QStandardItemModel>
HoverTreeView::HoverTreeView(QWidget *parent)
: QTreeView(parent)
{
setMouseTracking(true);
}
void HoverTreeView::mouseMoveEvent(QMouseEvent *event)
{
while (!_hoveredItems.empty())
{
HoverStandardItem* oldItem = _hoveredItems.pop();
oldItem->normal();
}
auto *item = static_cast<QStandardItemModel*>(model())->itemFromIndex(indexAt(event->pos()));
HoverStandardItem* realItem = static_cast<HoverStandardItem*>(item);
if (item) {
realItem->hovered();
_hoveredItems.push(realItem);
}
}
HoveredTreeView.h:
#ifndef HOVERTREEVIEW_H
#define HOVERTREEVIEW_H
#include <QStack>
#include <QTreeView>
#include "HoverStandardItem.h"
class HoverTreeView : public QTreeView
{
public:
HoverTreeView(QWidget *parent = nullptr);
public slots:
void mouseMoveEvent(QMouseEvent *event);
QStack<HoverStandardItem*> _hoveredItems;
};
#endif // HOVERTREEVIEW_H
HoveredStandardItem.cpp:
#include "HoverStandardItem.h"
HoverStandardItem::HoverStandardItem(QColor const& backgroundColor, const QString &text)
: QStandardItem(text)
, _backgroundColor(backgroundColor)
{
setData(backgroundColor, Qt::BackgroundColorRole);
}
void HoverStandardItem::hovered()
{
QColor hoveredColor(_backgroundColor);
unsigned int darker = 20;
hoveredColor.setRgb(hoveredColor.red() - darker, hoveredColor.green() - darker, hoveredColor.blue() - darker);
setData(hoveredColor, Qt::BackgroundColorRole);
}
void HoverStandardItem::normal()
{
setData(_backgroundColor, Qt::BackgroundColorRole);
}
HoveredStandardItem.h:
#ifndef HOVERSTANDARDITEM_H
#define HOVERSTANDARDITEM_H
#include <QStandardItem>
class HoverStandardItem : public QStandardItem
{
public:
HoverStandardItem(const QColor &backgroundColor, QString const& text = "");
void hovered();
void normal();
private:
QColor _backgroundColor;
};
#endif // HOVERSTANDARDITEM_H
我在主窗口中测试了它。这里的构造函数:
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
QStandardItemModel *model = new QStandardItemModel(this);
QColor warning[3] = {
{QColor(255, 86, 86)},
{QColor(86, 255, 86)},
{QColor(86, 86, 255)}
};
for (int j = 0 ; j < 3 ; ++j) {
QStandardItem *parentItem = model->invisibleRootItem();
for (int i = 0; i < 4; ++i) {
QStandardItem *item = new HoverStandardItem(warning[j], QString("item %0 %1").arg(j).arg(i));
parentItem->appendRow(item);
parentItem = item;
}
}
ui->treeView->setModel(model);
}
所以我自己解决了这个问题。 (毫无意义的赏金,我不知道为什么我在检查它是否有效之前交出了 50 个代表。)
我所做的是继承 QStyledItemDelegate
并重新实现 paint()
函数。
.h
class MyStyledItemDelegate : public QStyledItemDelegate
{
Q_OBJECT
public:
explicit MyStyledItemDelegate(QObject *parent = 0){}
virtual void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const;
}
在这个绘制函数中,我能够检查索引的 UserRoles 是否有自定义标志来决定我想要的颜色。我可以使用 QStyle::State_Selected
和 QStyle::State_MouseOver
检查是否选择了索引或 hovered.Using 该信息,我能够编写逻辑来确定我想要的颜色。之后我不得不手动绘制背景、图标和文本。
.cpp
void MyStyledItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
//background
QColor bgColor;
int bgColorType(0);
bgColorType = index.data(Qt::UserRole+9).toInt();//custom flag I set to determine which color i want
//color logic
if(bgColorType == 0)
bgColor = QColor(Qt::transparent);//default is transparent to retain alternate row colors
else if(bgColorType == 1)
bgColor = qRgba(237, 106, 106, 255);//red
else if(bgColorType == 2)
bgColor = qRgba(241, 167, 226, 255);//pink
//etc...
QStyleOptionViewItem opt(option);
if(option.state & QStyle::State_Selected)//check if item is selected
{
//more color logic
if(bgColorType == 0)
bgColor = qRgba(190, 220, 240, 255);
else
bgColor = qRgba(bgColor.red()-25, bgColor.green()-25, bgColor.blue()-25, 255);
//background color won't show on selected items unless you do this
opt.palette.setBrush(QPalette::Highlight, QBrush(bgColor));
}
if(option.state & QStyle::State_MouseOver)//check if item is hovered
{
//more color logic
bgColor = qRgba(bgColor.red()-25, bgColor.green()-25, bgColor.blue()-25, 255);
if(option.state & QStyle::State_Selected)//check if it is hovered AND selected
{
//more color logic
if(bgColorType == 0)
{
bgColor = qRgba(148, 200, 234, 255);
}
//background color won't show on selected items unless you do this
opt.palette.setBrush(QPalette::Highlight, QBrush(bgColor));
}
}
//set the backgroundBrush to our color. This affects unselected items.
opt.backgroundBrush = QBrush(bgColor);
//draw the item background
option.widget->style()->drawPrimitive(QStyle::PE_PanelItemViewItem, &opt, painter);
//icon
QRect iconRect = option.rect;
iconRect.setLeft(iconRect.left()+3);//offset it a bit to the right
//draw in icon, this can be grabbed from Qt::DecorationRole
//altho it appears icons must be set with setIcon()
option.widget->style()->drawItemPixmap(painter, iconRect, Qt::AlignLeft | Qt::AlignVCenter, QIcon(index.data(Qt::DecorationRole).value<QIcon>()).pixmap(16, 16));
//text
QRect textRect = option.rect;
textRect.setLeft(textRect.left()+25);//offset it a bit to the right
//draw in text, this can be grabbed from Qt::DisplayRole
option.widget->style()->drawItemText(painter, textRect, Qt::AlignLeft | Qt::AlignVCenter, option.palette, true, index.data(Qt::DisplayRole).toString());
}
完成后,我只需使用 myTreeView->setItemDelegate(new MyStyledItemDelegate(myTreeView));
将委托应用于我的 QTreeView
不需要样式表、后台角色更改或事件过滤器。我在整个互联网上搜索了一个解决方案,只发现很多人有同样的问题,但没有好的答案。这是迄今为止我想出的实现这一目标的最简单和最通用的方法,所以我希望它能帮助任何需要它的人:)
在我的项目中,我有几个 QTreeView
显示数据的小部件。 QTreeView
中项目的背景颜色根据数据类型和与其他项目的关联而变化。
这些背景颜色的设置方式如下:
QColor warning;
warning.setRgb(255, 86, 86);
model->itemFromIndex(index)->setData(warning, Qt::BackgroundRole);
这行得通,但是 我还想在项目为 selected/hovered 时使用不同的背景颜色。我选择使用样式表。
QTreeView::item:selected{background-color: #bedcf0;} //light blue
QTreeView::item:hover:selected{background-color: #94c8ea;} //darker blue
QTreeView::item:hover:!selected{background-color: #e6e6e6;} //gray
这提供了我想要的外观,但仅适用于具有白色默认背景的项目。如果项目具有自定义背景颜色(通过 Qt::BackgroundRole
设置),则这些悬停和选定颜色会完全覆盖当前背景颜色。
我想要发生的是在hovered/selected、基于当前背景时让每个项目变暗一个设定的量颜色。这很难,因为 QStandardItem::setProperty()
不存在。
感谢您的宝贵时间!
所以我有一个答案。也许你可以告诉我是否适合你 and/or 我们可以谈谈。
我创建了一个自定义 QTreeView
和 QStandardItem
,覆盖 mouseMoveEvent(QMouseEvent *event)
并设置了我的树 setMouseTracking(true);
。
我通过以下方式获得了鼠标下的项目:static_cast<QStandardItemModel*>(model())->itemFromIndex(indexAt(event->pos()))
这样我就可以得到悬停的项目。然后在我的自定义项目中,我有一个函数 hovered()
和 normal()
。当项目悬停时,将调用 hovered 方法。当鼠标移动时,它会将项目恢复正常并重新悬停它(如果它仍在上面)。
代码:
HoveredTreeView.cpp:
#include "HoverTreeView.h"
#include <QDebug>
#include <QMouseEvent>
#include <QStandardItemModel>
HoverTreeView::HoverTreeView(QWidget *parent)
: QTreeView(parent)
{
setMouseTracking(true);
}
void HoverTreeView::mouseMoveEvent(QMouseEvent *event)
{
while (!_hoveredItems.empty())
{
HoverStandardItem* oldItem = _hoveredItems.pop();
oldItem->normal();
}
auto *item = static_cast<QStandardItemModel*>(model())->itemFromIndex(indexAt(event->pos()));
HoverStandardItem* realItem = static_cast<HoverStandardItem*>(item);
if (item) {
realItem->hovered();
_hoveredItems.push(realItem);
}
}
HoveredTreeView.h:
#ifndef HOVERTREEVIEW_H
#define HOVERTREEVIEW_H
#include <QStack>
#include <QTreeView>
#include "HoverStandardItem.h"
class HoverTreeView : public QTreeView
{
public:
HoverTreeView(QWidget *parent = nullptr);
public slots:
void mouseMoveEvent(QMouseEvent *event);
QStack<HoverStandardItem*> _hoveredItems;
};
#endif // HOVERTREEVIEW_H
HoveredStandardItem.cpp:
#include "HoverStandardItem.h"
HoverStandardItem::HoverStandardItem(QColor const& backgroundColor, const QString &text)
: QStandardItem(text)
, _backgroundColor(backgroundColor)
{
setData(backgroundColor, Qt::BackgroundColorRole);
}
void HoverStandardItem::hovered()
{
QColor hoveredColor(_backgroundColor);
unsigned int darker = 20;
hoveredColor.setRgb(hoveredColor.red() - darker, hoveredColor.green() - darker, hoveredColor.blue() - darker);
setData(hoveredColor, Qt::BackgroundColorRole);
}
void HoverStandardItem::normal()
{
setData(_backgroundColor, Qt::BackgroundColorRole);
}
HoveredStandardItem.h:
#ifndef HOVERSTANDARDITEM_H
#define HOVERSTANDARDITEM_H
#include <QStandardItem>
class HoverStandardItem : public QStandardItem
{
public:
HoverStandardItem(const QColor &backgroundColor, QString const& text = "");
void hovered();
void normal();
private:
QColor _backgroundColor;
};
#endif // HOVERSTANDARDITEM_H
我在主窗口中测试了它。这里的构造函数:
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
QStandardItemModel *model = new QStandardItemModel(this);
QColor warning[3] = {
{QColor(255, 86, 86)},
{QColor(86, 255, 86)},
{QColor(86, 86, 255)}
};
for (int j = 0 ; j < 3 ; ++j) {
QStandardItem *parentItem = model->invisibleRootItem();
for (int i = 0; i < 4; ++i) {
QStandardItem *item = new HoverStandardItem(warning[j], QString("item %0 %1").arg(j).arg(i));
parentItem->appendRow(item);
parentItem = item;
}
}
ui->treeView->setModel(model);
}
所以我自己解决了这个问题。 (毫无意义的赏金,我不知道为什么我在检查它是否有效之前交出了 50 个代表。)
我所做的是继承 QStyledItemDelegate
并重新实现 paint()
函数。
.h
class MyStyledItemDelegate : public QStyledItemDelegate
{
Q_OBJECT
public:
explicit MyStyledItemDelegate(QObject *parent = 0){}
virtual void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const;
}
在这个绘制函数中,我能够检查索引的 UserRoles 是否有自定义标志来决定我想要的颜色。我可以使用 QStyle::State_Selected
和 QStyle::State_MouseOver
检查是否选择了索引或 hovered.Using 该信息,我能够编写逻辑来确定我想要的颜色。之后我不得不手动绘制背景、图标和文本。
.cpp
void MyStyledItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
//background
QColor bgColor;
int bgColorType(0);
bgColorType = index.data(Qt::UserRole+9).toInt();//custom flag I set to determine which color i want
//color logic
if(bgColorType == 0)
bgColor = QColor(Qt::transparent);//default is transparent to retain alternate row colors
else if(bgColorType == 1)
bgColor = qRgba(237, 106, 106, 255);//red
else if(bgColorType == 2)
bgColor = qRgba(241, 167, 226, 255);//pink
//etc...
QStyleOptionViewItem opt(option);
if(option.state & QStyle::State_Selected)//check if item is selected
{
//more color logic
if(bgColorType == 0)
bgColor = qRgba(190, 220, 240, 255);
else
bgColor = qRgba(bgColor.red()-25, bgColor.green()-25, bgColor.blue()-25, 255);
//background color won't show on selected items unless you do this
opt.palette.setBrush(QPalette::Highlight, QBrush(bgColor));
}
if(option.state & QStyle::State_MouseOver)//check if item is hovered
{
//more color logic
bgColor = qRgba(bgColor.red()-25, bgColor.green()-25, bgColor.blue()-25, 255);
if(option.state & QStyle::State_Selected)//check if it is hovered AND selected
{
//more color logic
if(bgColorType == 0)
{
bgColor = qRgba(148, 200, 234, 255);
}
//background color won't show on selected items unless you do this
opt.palette.setBrush(QPalette::Highlight, QBrush(bgColor));
}
}
//set the backgroundBrush to our color. This affects unselected items.
opt.backgroundBrush = QBrush(bgColor);
//draw the item background
option.widget->style()->drawPrimitive(QStyle::PE_PanelItemViewItem, &opt, painter);
//icon
QRect iconRect = option.rect;
iconRect.setLeft(iconRect.left()+3);//offset it a bit to the right
//draw in icon, this can be grabbed from Qt::DecorationRole
//altho it appears icons must be set with setIcon()
option.widget->style()->drawItemPixmap(painter, iconRect, Qt::AlignLeft | Qt::AlignVCenter, QIcon(index.data(Qt::DecorationRole).value<QIcon>()).pixmap(16, 16));
//text
QRect textRect = option.rect;
textRect.setLeft(textRect.left()+25);//offset it a bit to the right
//draw in text, this can be grabbed from Qt::DisplayRole
option.widget->style()->drawItemText(painter, textRect, Qt::AlignLeft | Qt::AlignVCenter, option.palette, true, index.data(Qt::DisplayRole).toString());
}
完成后,我只需使用 myTreeView->setItemDelegate(new MyStyledItemDelegate(myTreeView));
QTreeView
不需要样式表、后台角色更改或事件过滤器。我在整个互联网上搜索了一个解决方案,只发现很多人有同样的问题,但没有好的答案。这是迄今为止我想出的实现这一目标的最简单和最通用的方法,所以我希望它能帮助任何需要它的人:)