2016-02-01 2 views
3

Qt позволяет вам стилизовать GUI с помощью таблиц стилей. Они используют аналогичный синтаксис выбора для обычного CSS. Я хотел бы использовать эти селекторы в коде для достижения определенных виджетов, подобно тому, как работает jQuery. Например:Получить QWidget * с помощью селектора CSS (QSS)

SomeQtCSSMagic::resolveSelector("QPushButton#okButton"); 

Возможно ли это? Если нет, есть ли хотя бы код, который я мог бы скопировать-вставить из библиотеки Qt и отредактировать в соответствии с моими потребностями?

ответ

4

В настоящий момент 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. Помимо парсера, вам также нужен код для использования синтаксического селектора для поиска виджета. А именно, вы должны реализовать QCss::StyleSelector для узла, который указывает на QObject. Это делается QStyleSheetStyleSelector в src/widgets/styles/qstylesheetstyle.cpp, но это локальный класс, который не экспортируется, поэтому у вас есть своя копия, чтобы справиться с этим.

Как только вы внедрили 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 
+0

Хорошо, кажется я реализовать это сам потом. Спасибо, что указали мне в правильном направлении относительно проверки виджетов. –

+0

@ TomášZato Qt уже реализует его. С небольшим количеством копий-макарон вы можете использовать это. Я обновил ответ. –

+0

Действительно потрясающе! :) – Tarod

Смежные вопросы