使用 CSS (QSS) 选择器获取 QWidget*
Get QWidget* using a CSS (QSS) selector
Qt允许您使用样式表设置GUI样式。它们使用与常规CSS类似的选择器语法。我想在代码中使用这些选择器来访问特定的小部件,类似于jQuery的工作方式。例如:
SomeQtCSSMagic::resolveSelector("QPushButton#okButton");
这可能吗?如果没有,是否至少有代码我可以从Qt库中复制粘贴并进行编辑以满足我的需求?
目前,Qt没有提供官方支持。但是使用纯C++代码并不难做到。
您的选择器将转换为:
for (auto top : qApp->topLevelWidgets()) {
auto widgets = top->findChildren<QWidget*>("okButton");
widgets << top;
for (auto widget : widgets)
if (widget->inherits("QPushButton"))
qDebug() << "found" << widget;
}
如果您发现此类代码很乏味,则可以利用私有Qt代码。CSS 解析器接口位于 src/gui/text/qcssparser_p.h 中。除了解析器之外,您还需要一些代码来使用解析的选择器来查找小部件。也就是说,您必须为指向QObject
的节点实现QCss::StyleSelector
。这是通过在src/widgets/styles/qstylesheetstyle.cpp中QStyleSheetStyleSelector
完成的,但这是一个未导出的本地类,因此您确实有一些copypasta来处理它。
一旦你实现了QCss::StyleSelector
,这里作为QObjectStyleSelector
,获取选择器适用的小部件很容易。你必须遍历所有小部件,看看CSS机制是否有给定节点的任何规则,假设一个由你的选择器和一个空的主体组成的虚拟样式表{}
。此方法支持样式表支持的所有选择器,但选择小部件的样式除外:
QWidgetList select(const QString & selector) {
static QHash<QString, StyleSheet> cache;
QWidgetList result;
QObjectStyleSelector oSel;
auto it = cache.find(selector);
if (it == cache.end()) {
StyleSheet styleSheet;
Parser parser(selector + "{}");
if (!parser.parse(&styleSheet))
return result;
it = cache.insert(selector, styleSheet);
}
oSel.styleSheets.append(*it);
for (auto top : qApp->topLevelWidgets()) {
auto widgets = top->findChildren<QWidget*>();
widgets << top;
for (auto widget : widgets) {
StyleSelector::NodePtr n { widget };
auto rules = oSel.styleRulesForNode(n);
if (!rules.isEmpty()) result << widget;
}
}
return result;
}
上面的代码不会被小部件上可能存在的任何样式表"分散"注意力 - 它考虑了它们的副作用(例如属性更改(,但会忽略它们。
您可以按如下方式对其进行测试:
int main(int argc, char ** argv) {
QApplication app{argc, argv};
QDialog dialog;
QPushButton button{"OK", &dialog};
button.setObjectName("okButton");
button.setStyleSheet("color: red");
auto dialog_l = QWidgetList() << &dialog;
auto button_l = QWidgetList() << &button;
auto all_l = button_l + dialog_l;
Q_ASSERT(select("QPushButton#okButton") == button_l);
Q_ASSERT(select("QDialog QPushButton") == button_l);
Q_ASSERT(select("QDialog") == dialog_l);
Q_ASSERT(select("QDialog, QPushButton") == all_l);
}
复制面食在上述内容之前:
// https://github.com/KubaO/stackoverflown/tree/master/questions/css-selector-35129103
#include <QtWidgets>
#include <private/qcssparser_p.h>
using namespace QCss;
// FROM src/widgets/styles/qstylesheetstyle.cpp
#define OBJECT_PTR(node) (static_cast<QObject*>((node).ptr))
static inline QObject *parentObject(const QObject *obj)
{
if (qobject_cast<const QLabel *>(obj) && qstrcmp(obj->metaObject()->className(), "QTipLabel") == 0) {
QObject *p = qvariant_cast<QObject *>(obj->property("_q_stylesheet_parent"));
if (p)
return p;
}
return obj->parent();
}
class QObjectStyleSelector : public StyleSelector
{
public:
QObjectStyleSelector() { }
QStringList nodeNames(NodePtr node) const Q_DECL_OVERRIDE
{
if (isNullNode(node))
return QStringList();
const QMetaObject *metaObject = OBJECT_PTR(node)->metaObject();
#ifndef QT_NO_TOOLTIP
if (qstrcmp(metaObject->className(), "QTipLabel") == 0)
return QStringList(QLatin1String("QToolTip"));
#endif
QStringList result;
do {
result += QString::fromLatin1(metaObject->className()).replace(QLatin1Char(':'), QLatin1Char('-'));
metaObject = metaObject->superClass();
} while (metaObject != 0);
return result;
}
QString attribute(NodePtr node, const QString& name) const Q_DECL_OVERRIDE
{
if (isNullNode(node))
return QString();
auto obj = OBJECT_PTR(node);
auto value = obj->property(name.toLatin1());
if (!value.isValid()) {
if (name == QLatin1String("class")) {
auto className = QString::fromLatin1(obj->metaObject()->className());
if (className.contains(QLatin1Char(':')))
className.replace(QLatin1Char(':'), QLatin1Char('-'));
return className;
}
}
if(value.type() == QVariant::StringList || value.type() == QVariant::List)
return value.toStringList().join(QLatin1Char(' '));
else
return value.toString();
}
bool nodeNameEquals(NodePtr node, const QString& nodeName) const Q_DECL_OVERRIDE
{
if (isNullNode(node))
return false;
auto metaObject = OBJECT_PTR(node)->metaObject();
#ifndef QT_NO_TOOLTIP
if (qstrcmp(metaObject->className(), "QTipLabel") == 0)
return nodeName == QLatin1String("QToolTip");
#endif
do {
const ushort *uc = (const ushort *)nodeName.constData();
const ushort *e = uc + nodeName.length();
const uchar *c = (const uchar *)metaObject->className();
while (*c && uc != e && (*uc == *c || (*c == ':' && *uc == '-'))) {
++uc;
++c;
}
if (uc == e && !*c)
return true;
metaObject = metaObject->superClass();
} while (metaObject != 0);
return false;
}
bool hasAttributes(NodePtr) const Q_DECL_OVERRIDE
{ return true; }
QStringList nodeIds(NodePtr node) const Q_DECL_OVERRIDE
{ return isNullNode(node) ? QStringList() : QStringList(OBJECT_PTR(node)->objectName()); }
bool isNullNode(NodePtr node) const Q_DECL_OVERRIDE
{ return node.ptr == 0; }
NodePtr parentNode(NodePtr node) const Q_DECL_OVERRIDE
{ NodePtr n; n.ptr = isNullNode(node) ? 0 : parentObject(OBJECT_PTR(node)); return n; }
NodePtr previousSiblingNode(NodePtr) const Q_DECL_OVERRIDE
{ NodePtr n; n.ptr = 0; return n; }
NodePtr duplicateNode(NodePtr node) const Q_DECL_OVERRIDE
{ return node; }
void freeNode(NodePtr) const Q_DECL_OVERRIDE
{ }
};
// END FROM
#cssparser-35129103.pro
QT = widgets gui-private
CONFIG += c++11
TARGET = cssparser-35129103
TEMPLATE = app
SOURCES += main.cpp
相关文章:
- C++为构建时间获取QDateTime的可靠方法
- lambda参数转换为constexpr技巧,然后获取带链接的数组
- 如何使用 < 和 > 命令获取 c++ 中的输入和输出?
- 使用指针从C++中的数组中获取最大值
- 如何获取std::result_of函数的返回类型
- 如何在openssl-ecc中获取十六进制格式的私钥
- 使用Unreal C++获取VR耳机的世界位置/方向
- 获取日期异步信号安全吗?如果在信号处理程序中使用,它会导致死锁吗
- 从C字符串中获取奇怪的字符串长度
- 为什么我的for循环不能正确获取argv
- 从python中调用C++函数并获取返回值
- 如何获取一个数字的前3位
- 获取字符串的长度并将其分配给数组
- 如何在QWidget中获取QtDataVizualization的QPixmap并保存到文件中?
- 如何在Qwidget上获取当前的鼠标光标位置
- 如何在QWidget中获取样式表更改事件
- 如何获取响应QVBoxLayout中QWidget的当前宽度
- 从QWidget获取对象名称(如Qt Designer中所示)
- 使用 CSS (QSS) 选择器获取 QWidget*
- 获取 QWidget 的 Windows 消息而不对其进行子类化并重新实现 QWidget::winEvent