Бесіди про qt: qsettings і xml

Бесіди про Qt: QSettings і XML
Клас QSettings служить Qt-програмістам вірою і правдою, коли потрібно зберегти або завантажити будь-які налаштування програми. Технічні турботи по зберіганню налаштувань клас бере на себе. У Linux за замовчуванням створюється ini-подібний файл в каталозі $ HOME / .config / назва програми, а в Windows записи поміщаються до реєстру. Таку поведінку можна перевизначити, викликавши конструктор з ім`ям файлу і вказавши формат: в разі QSettings :: IniFormat і в Windows буде використаний ini-подібний файл.

З точки зору доступу до даних різниці немає ніякої: для читання і запису використовуються одні й ті ж функції незалежно від фізичного способу зберігання даних. Приклад читання:
QString s = settings-gt; value ( "my string", "my default value"). ToString () -

Приклад запису:
QString s-settings-gt; setValue ( "my string", s) -

Крім таких буденних речей, як зберігання «одиничних» змінних, QSettings надає потужний механізм збереження інших даних, наприклад стану QSplitter (тобто розмірів віджетів, вставлених в спліттер), а також стану допоміжних віджетів головного вікна, таких як інструментальних панелей.

У класу QMainWindow є методи restoreState і saveState. Для збереження досить викликати при закритті вікна:
settings-gt; setValue ( "state", saveState ()) -

Для відновлення можна помістити в конструктор наступне:
restoreState (settings-gt; value ( "state", QByteArray ()) .toByteArray ()) -

В результаті буде відновлено положення панелей інструментів, причому якщо вони були не припарковані, то цей стан теж зберігається і відновлюється. Зауважу, що для правильної роботи цього механізму панелей управління треба спочатку дати імена, а потім вже зберігати або відновлювати. наприклад:
tb _ my _ toolbar = new QToolBar-tb _ my _ toolbar-gt; setObjectName ( "tb _ my _ toolbar") -

До спліттер це не відноситься. Стан спліттера відновлюється приблизно так:
my _ splitter-gt; restoreState (settings-gt; value ( "splitterSizes"). toByteArray ()) -

А зберігається в такий спосіб:



settings-gt; setValue ( "splitterSizes", my _ splitter-gt; saveState ()) -

Чудова властивість QSettings зберігати і завантажувати дані будь-яких типів пояснюється тим, що записуються і повертаються екземпляри не менш чудового класу QVariant. Можливо, ви хочете що завгодно: від рядка або числа до цілої хеш-таблиці. Важко уявити для зберігання пар формату «ключ = значення» що-небудь зручніше, ніж клас QSettings, однак, якщо виникне потреба написати своє рішення, це неважко зробити при наявності інших класів зі складу Qt. Всього-то й потрібно, що прочитати текст з файлу, розбити його на рядки, розібрати пари з кожного рядка і помістити їх - для прискорення доступу - в хеш-таблицю.

Для читання тексту з файлу можна використовувати таку функцію:
QString qstring _ load (const QString SfileName, const QString Scharset) QFile :: Text)) return QString () - QTextStream in (Sfile) -in. setCodec (charset. toAscii ()) - return in.readAll () -

Ось як читати текст з файлу:
QString text = qstring _ load ( "/test/test.txt", "cp1251") -

Тепер - створення списку з отриманого тексту, припускаючи, що в тексті кожен рядок відокремлена від іншої за допомогою «n»:



QStringList 1 = text.split ( "n") -

Для стислості можна ці дві дії об`єднати в одне:
QStringList 1 = qstring _load (fname) .split ( "n") -

Що трапиться, якщо повернута від qstring_load рядок раптом виявиться порожньою? Нічого - адже це не null-рядок як покажчик, а екземпляр класу. Повернеться цілком готовий клас QString, і не його вина, що тексту-то в ньому може не бути, і ділити на рядки нічого. При спробі розібрати на рядки таку «порожню» рядок буде повернутий QStringList з нулем елементів.

Перетворення QStringList в хеш-таблицю робиться просто. Уявімо, що ми хочемо трактувати не лише ключі (з прочитаного файлу), але і зіставлені їм значення як рядка. Такому стану речей буде відповідати хеш-таблиця наступного вигляду:
QHash my _ table-

Напишемо функцію, яка отримує в якості параметра ім`я файлу, завантажує з нього «ini-дані» і повертає їх як готову до використання хеш-таблицю:
QHash hash _ load _ keyval (const QString Sfname) {// Сюди будемо записувати розібрані рядки: QHash result - // Читаємо з файлу в список: QStringList l = qstring _load (fname) .split ( "n") - // Проходимо за всім списком: foreach (QString s, l) {// Ділимо поточний рядок списку на два елементи, // розділені знаком рівності: QStringList sl = s.split ( "=") - // Якщо елементів більше 1, все правильно, // вставляємо ключ і значення в хеш-таблицю if (sl.size () gt; 1) result.insertMulti (sl [0], sl [1]) -} // Повертаємо заповнену таблицю: return result-}

Зверніть увагу на функцію QHash :: insertMulti. Замість неї можна було б використовувати просто insert, але в такому випадку втрачаються всі повторювані значення (для ключа), крім останнього.

Збереження хеш-таблиці у файлі виглядає приблизно так: отримуємо список унікальних ключів QList [Key] QHash :: uniqueKeys (), потім для кожного з них отримуємо список значень QList [T] QHash :: values (const Key key), формуємо такі рядки «ключ = значення» і записуємо їх в QStringList, після чого викликаємо QStringList :: join ( «n»), щоб отримати готову рядок з усіма даними хеш-таблиці. Тепер цей рядок можна зберегти в файл, причому добре б відрізати останній символ рядка (там знаходиться зайвий «n»).

Тепер - про XML. До XML звертаються, як правило, в двох випадках: коли зовсім незручно зберігати дані у форматі «ключ = значення» або коли під рукою є хороший XML-парсер і дуже вже хочеться його випробувати. За великим рахунком там, де можна обійтися ini-подібними файлами, немає потреби стріляти з гармати по горобцях. XML хороший там, де треба зберігати і завантажувати деревовидні структури даних. Qt дає цілих три механізму роботи з XML. Перший називається SAX і надає спосіб роботи з XML, заснований на подіях. Тобто запускається парсер і на виникаючі при розборі XML-даних події (наприклад, відкриття тега) викликаються задані вами обробники. Для роботи методом SAX у Qt служить QXmlSimpleReader і супутні йому класи.

Інший механізм - QDomDocument. Підхід тут інший: парсеру передається весь текст документа, який відразу розбирається, і вам залишається «ходити» по вже розібраному дереву даних, доступному всередині QDomDocument. Зупинюся на цьому докладніше. Припустимо, ми хочемо написати простий парсер документів OpenDocument Text (ODT). Нагадаю, що ODT - це звичайний ZIP-архів, наповнений файлами як з текстом / зображеннями, так і службового призначення. Власне текст знаходиться в файлі content.xml. Розпакування ZIP-архівів виходить за рамки цієї статті. Припустимо, що ми вже пройшли етап розпакування і можемо передати парсеру вміст файлу content.xml. Напишемо клас CODTXMLWalker, який буде витягувати весь текст з XML-дерева. У класі нам знадобляться наступні поля:
- QString data - сюди ми будемо розміщувати параграфи тексту, витягнуті з вузлів дерева-
- QDomDocument doc - екземпляр парсеру.

Важлива функція void step (QDomNode node) - вона послужить для ітераційного проходження по всіх елементах дерева, на всіх його рівнях. Отже, оголошення класу:
class CODTXMLWalker: public QObject {Q _ OBJECT public: QString data- QDomDocument doc-void step (QDomNode node) -} -

Бесіди про Qt: QSettings і XML

Відео: C ++ Qt 95 - Read XML with DOM


Втілення функції step:
void CODTXMLWalker :: step (QDomNode node) {// Для всіх дітей вузла node проходимо в циклі: for (QDomNode n = node.firstChild () -! n.isNull () - n = n.nextSibling ()) {// перетворимо вузол (поточний «дитя») в елемент: QDomElement e = n.toElement () - // Якщо елемент нічого не містить, переходимо до наступного: if (e.isNull ()) continue - // Якщо ім`я вузла одно «text : s », у нас можливий відступ: if (e.nodeName () ==" text: s ") {// Перевіряємо наявність відступу: QString indent = e.attribute (" text: c ") - // Якщо є відступ , створюємо рядок з прогалин у кількості // значень відступу: if (! indent.isEmpty ()) {QString fillval-fillval = fillval.fill ( `indent.toInt ()) - // І додаємо цей рядок до data: data. append (fillval) -}} else // Інакше, якщо такі-то імена у лов, то у нас параграф або заголовок if (e.nodeName () == "text: p" || e.nodeName () == "text: h") {// Якщо текст в них не порожній, додаємо його до data if (! e.text (). isEmpty ()) {data.append (e.text ()) - data.append ( "n") -}} // Якщо у вузла є діти, робимо рекурсивний виклик step, // щоб цих дітей теж обробити: if (e.hasChildNodes ()) step (n) -}}

Приклад використання нашого парсеру:
QString string _ data = // Читаємо сюди вміст content.xml // Створюємо парсер: CODTXMLWalker walker - // Встановлюємо йому вміст, що викликає розбір оного: walker-gt; doc.setContent (string _ data) - // Тепер проходимо по всіх елементів розібраного XML-файла: walker-gt; step (walker-gt; doc.documentElement ()) - // Виводимо витягнутий текст на консоль: qDebug () lt; lt; walker-gt; data-

Звичайно ж, дерево об`єктів можна змінювати (наприклад, додавати в нього нові елементи), а потім отримати готовий XML-файл у вигляді текстового об`єкта, викликавши функцію QDomDocument :: toString ().

Бесіди про Qt: QSettings і XML

Відео: C ++ Qt 93 - Basic XML Primer


Нарешті, в Qt існує третій спосіб роботи з XML, який має на увазі використання класів QXmlStreamReader і QXmlStreamWriter. Обидва «заточені» на роботу з рекурсією, хоча самі класи обробляють дані послідовно. Що мається на увазі? Припустимо, ми рекурсивно обходимо якесь дерево і по ходу справи пишемо в XML-потік (це може бути файл, буфер в пам`яті тощо) елементи. А при читанні ми послідовно отримуємо елементи, один за іншим, «знайомлячись» з ними в міру їх вкладеності. Щоб перевірити, чи є чергова розібрана складова потоку початком елемента, досить викликати функцію isStartElement (). Якщо вона повертає істину - ми знаходимося за відкриває тегом. Для перевірки на закриття тега є парна функція - isEndElement (). У прикладі нижче показано читання XML-файла і розбір його на елементи. У циклі розбору на консоль виводиться назва чергового елемента, а також список його атрибутів і їх значень:
QFile file (filename) -if (! File.open (QIODevice :: ReadOnly | QIODevice :: Text)) return-QXmlStreamReader xml (Sfile) -while (! Xml.atEnd ()) {xml.readNext () - if ( xml.isStartElement ()) {qDebug () lt; lt; xml.name (). toString () - QXmlStreamAttributes attrs = xml.QXmlStreamReader :: attributes () - for (int i = 0 i lt; attrs.count () - qDebug () lt; lt; attrs [i] .name (). toString () lt; lt; "=" lt; lt; attrs [i] .value (). toString () -}}

Саме цей, третій, спосіб роботи з XML здається мені найбільш зручним при збереженні / завантаженні яких-небудь налаштувань або для побудови інтерфейсу на основі XML. Адже якщо у випадку з QDomDocument ви після розбору XML повинні пройтися рекурсивно по всьому дереву, то тут можете що-небудь робити прямо під час розбору. А QDomDocument більш придатний, коли потрібно саме робота з елементами дерева, а не просто зберігання і завантаження.

ІНШЕ

Ultimate ++: схеми баз даних фото

Ultimate ++: схеми баз даних

Відео: Бази даних, лекція №1 (2013) Об`єкти SqlExp кардинально полегшують створення запитів, але створення самих…

Неясності з phonon фото

Неясності з phonon

Від труднощів збірки перейдемо до неясним місцях у використанні Phonon. Phonon підтримує ті мультимедійні формати, які…

» » Бесіди про qt: qsettings і xml