2015-07-23 8 views
8

Ho un documento in formato * .css (Cascading Style Sheets), ma ha le sue parole chiave. In realtà è un css personalizzato (lo chiamo * .pss), con propri tag e proprietà. qui ho un estratto:Come posso semplicemente analizzare un file CSS come (!) Nella mia applicazione Qt?

/* CSS like style sheet file *.pss */ 

@include "otherStyleSheet.pss"; 

/* comment */ 
[propertyID="1230000"] { 
    fillColor : #f3f1ed; 
    minSize : 5; 
    lineWidth : 3; 
} 

/* sphere */ 
[propertyID="124???|123000"] { 
    lineType : dotted; 
} 

/* square */ 
[propertyID="125???"] { 
    lineType : thinline;  
} 

/* ring */ 
[propertyID="133???"] { 
    lineType : thickline; 
    [hasInnerRing=true] { 
    innerLineType : thinline; 
    } 
} 

vorrei analizzare molto facilmente, c'è già qualcosa di pronto da Usa Qt? Quale sarebbe il modo più semplice?

Poiché * .css ha le sue parole chiave, NON sono interessato ai parser CSS.

La mia ulteriore intenzione dopo l'analisi di * .pss è di memorizzare le sue proprietà in una struttura Model.

+0

Non sono a conoscenza di nulla disponibile. Sarebbe un vero e proprio CSS o CSS di cui hai il controllo? –

+0

@FrankOsterfeld Mi spiace, non conosco la differenza fuori dagli schemi e il controllo? Ho modificato la mia domanda .. Thnx –

+0

Ciò che Frank intende è: il CSS è una parte della tua applicazione, in modo che tu possa modificarla e gestirla, o provenga da fonti potenzialmente dannose su Internet? Ricorda che se il tuo parser CSS ha dei bug, i CSS dannosi possono sfruttarli e prendere in carico la tua applicazione. In un codice C++ (o C) meno che moderno, tali bug sono più la norma che l'eccezione. Il parser di Qt non è progettato per essere resistente ai CSS dannosi, non è mai stato progettato per accettare input casuali da Internet. Ha un sacco di buchi di sicurezza, ne sono sicuro. –

risposta

1

Conosco due possibilità:

  1. boost::spirit e here è possibile trovare una buona introduzione al boost :: quadro spirito parser
  2. consiglierei di scrivere il proprio recursive descent parser

causa al fatto che il tuo * .pss personalizzato non è così complesso come un CSS (parentesi semplice ecc.), ti consiglio 2.

+0

I semplici esempi di parser ricorsivo di discesa che ho trovato, sembrano abbastanza promettenti. boost: lo spirito sembra essere sviluppato su questo. Thnx. –

8

Non c'è nulla di pubblico in Qt. Ovviamente sei libero di utilizzare il parser CSS privato di Qt: puoi copiarlo e modificarlo in base alle tue esigenze.

Vedere qtbase/src/gui/text/qcssparser_p.h, in qtbase/src/gui/text.

La buona notizia è che per l'esempio che hai mostrato sopra, le modifiche sarebbero molto minori. Il parser CSS di Qt supporta già @import, quindi abbiamo solo un ulteriore bit di sintassi che possiedi è il nested selector syntax. Senza questa sintassi, è possibile utilizzare QCss::Parser così com'è. Il parser è stato scritto in modo flessibile, in cui non è necessario preoccuparsi delle parole chiave formali CSS: consente comunque di accedere a tutte le dichiarazioni, indipendentemente dal loro punto di vista formale o meno.

Iterando l'albero sintattico è così semplice come si arriva:

int main() { 
    QCss::Parser parser(pss); 
    QCss::StyleSheet styleSheet; 
    if (!parser.parse(&styleSheet)) 
     return 1; 
    for (auto rule : styleSheet.styleRules) { 
     qDebug() << "** Rule **"; 
     for (auto sel : rule.selectors) { 
     for (auto bSel : sel.basicSelectors) 
      qDebug() << bSel; 
     } 
     for (auto decl : rule.declarations) 
     qDebug() << decl; 
    } 
} 

L'uscita è quello che ci si aspetterebbe:

** Rule ** 
BasicSelector "propertyID"="1230000" 
Declaration "fillColor" = '#f3f1ed' % QColor(ARGB 1, 0.952941, 0.945098, 0.929412) 
Declaration "minSize" = '5' % 5 
Declaration "lineWidth" = '3' 
** Rule ** 
BasicSelector "propertyID"="124???|123000" 
Declaration "lineType" = 'dotted' 
** Rule ** 
BasicSelector "propertyID"="125???" 
Declaration "lineType" = 'thinline' 
** Rule ** 
BasicSelector "propertyID"="133???" 
Declaration "lineType" = 'thickline' 

Dobbiamo implementare gli operatori del flusso di debug per QCss classi noi stessi :

QDebug operator<<(QDebug dbg, const QCss::AttributeSelector & sel) { 
    QDebugStateSaver saver(dbg); 
    dbg.noquote().nospace() << "\"" << sel.name << "\""; 
    switch (sel.valueMatchCriterium) { 
    case QCss::AttributeSelector::MatchEqual: 
     dbg << "="; break; 
    case QCss::AttributeSelector::MatchContains: 
     dbg << "~="; break; 
    case QCss::AttributeSelector::MatchBeginsWith: 
     dbg << "^="; break; 
    case QCss::AttributeSelector::NoMatch: 
     break; 
    } 
    if (sel.valueMatchCriterium != QCss::AttributeSelector::NoMatch && !sel.value.isEmpty()) 
     dbg << "\"" << sel.value << "\""; 
    return dbg; 
} 

QDebug operator<<(QDebug dbg, const QCss::BasicSelector & sel) { 
    QDebugStateSaver saver(dbg); 
    dbg.noquote().nospace() << "BasicSelector"; 
    if (!sel.elementName.isEmpty()) 
     dbg << " #" << sel.elementName; 
    for (auto & id : sel.ids) 
     dbg << " id:" << id; 
    for (auto & aSel : sel.attributeSelectors) 
     dbg << " " << aSel; 
    return dbg; 
} 

Quando si attraversa la dichiarazione, il QCss::parser interpreta già per noi alcuni valori standard, ad es. i colori, numeri interi, ecc

QDebug operator<<(QDebug dbg, const QCss::Declaration & decl) { 
    QDebugStateSaver saver(dbg); 
    dbg.noquote().nospace() << "Declaration"; 
    dbg << " \"" << decl.d->property << "\" = "; 
    bool first = true; 
    for (auto value : decl.d->values) { 
     if (!first) dbg << ", "; 
     dbg << "\'" << value.toString() << "\'"; 
     first = false; 
    } 
    if (decl.d->property == "fillColor") 
     dbg << " % " << decl.colorValue(); 
    else if (decl.d->property == "minSize") { 
     int i; 
     if (decl.intValue(&i)) dbg << " % " << i; 
    } 
    return dbg; 
} 

Infine, il boilerplate e il foglio di stile da analizzare:

// https://github.com/KubaO/stackoverflown/tree/master/questions/css-like-parser-31583622 
#include <QtGui> 
#include <private/qcssparser_p.h> 

const char pss[] = 
    "/* @include \"otherStyleSheet.pss\"; */ \ 
    [propertyID=\"1230000\"] { \ 
    fillColor : #f3f1ed; \ 
    minSize : 5; \ 
    lineWidth : 3; \ 
    } \ 
    \ 
    /* sphere */ \ 
    [propertyID=\"124???|123000\"] { \ 
    lineType : dotted; \ 
    } \ 
    \ 
    /* square */ \ 
    [propertyID=\"125???\"] { \ 
    lineType : thinline; \ 
    } \ 
    \ 
    /* ring */ \ 
    [propertyID=\"133???\"] { \ 
    lineType : thickline; \ 
    /*[hasInnerRing=true] { \ 
     innerLineType : thinline; \ 
    }*/ \ 
    }"; 

supporto per selettori nidificate/regole può essere implementata modificando la fonte parser. La modifica necessaria per rendere ricorsiva Parser::parseRuleset è molto minore. Lascerò questo come esercizio per il lettore :)

Tutto sommato, penserei che riutilizzare il parser esistente sia molto più facile che farlo da solo, soprattutto perché gli utenti vorranno inevitabilmente supportare di più e più delle specifiche CSS.

+0

https://github.com/qtproject/qtbase/blob/dev/src/gui/text/qcssparser.cpp ha oltre 2800 righe di codice. –

+0

@RalfWickum E cosa c'è di sbagliato in questo? Voglio dire - è un lexer/parser CSS completo con un albero di sintassi concreto. Non si dovrebbe modificare tutte le 2800 linee, apportare le modifiche necessarie e utilizzarle integralmente. Implementando te stesso, ti ritroverai con la stessa quantità di codice che non è stato testato in modo così approfondito. La leva che ottieni da Qt è che l'intera base di codice è tua per l'uso a tuo piacimento. Che ce ne sia un sacco è una cosa buona, non una brutta cosa :) –

+0

Ho dato un'occhiata a tutti i file richiesti come: https://github.com/qtproject/qtbase/blob/dev/src/gui/text/ qcssparser_p.h, https://github.com/qtproject/qtbase/blob/dev/src/gui/text/qcssparser.cpp e https://github.com/qtproject/qtbase/blob/dev/src/gui/ text/qcssscanner.cpp. Sono molto più comodo scrivere un totale di 4000 linee di codice invece di importarlo senza capire cosa sta succedendo lì. Non dico che il parser è cattivo. Era troppo complesso per me. –

1

Bene, immagino che non si voglia dedicarsi alla scrittura di un parser di oggetti, si potrebbe semplicemente reinventare JSON, o YAML o simili. Quindi la soluzione migliore è rendere la formattazione conforme a una configurazione nota o linguaggio di notazione degli oggetti e quindi analizzarla con una libreria per la lingua che si sta utilizzando.Con la modifica molto minore, il formato che si descrive sopra potrebbe diventare HOCON, che è una bella superset di JSON, e ha la sintassi molto più vicino a quello che si sta utilizzando:

https://github.com/typesafehub/config/blob/master/HOCON.md

si potrebbe poi analizzarlo con un La libreria di analisi di HOCON e voilà, tu avresti degli oggetti in memoria che puoi modellare o archiviare come preferisci. Credo che Qt sia basato sul C++? C'è una libreria hocon per C, non so di C++, e suppongo che tu abbia bisogno di scrivere un plug-in Qt per avvolgere l'analisi HOCON da un'altra lingua.

L'altra opzione è quella di utilizzare un parser CSS-> oggetto come questo: https://github.com/reworkcss/css

Quale potrebbe essere necessario sborsare e modificare le vostre esigenze. In ogni caso, suppongo che per integrarsi in un'applicazione Qt sia necessario un plug-in che gestisca alcune chiamate a un processo da riga di comando o ad un altro modulo di codice.

+0

Farei anche quel formato di file in modo diverso. Ma dal momento che abbiamo ottenuto quel formato da un altro dipartimento, non sto andando (e non sono in grado) di cambiarlo. –

Problemi correlati