Qt: крик души
Как-то я привык, что в Qt есть все, что нужно разумному человеку, но жизнь оказалась богаче.
Представим себе какой-то обычный GUI(Windows)-диалог с настройками. Там галочки, пимпочки, выпадающие списки, общим числом с полсотни-сотню, например (5 табов по 10-15 параметров, так и получится).
Ну, понятно, по OK значения в программе апдейтятся, по Cancel - остаются как были, а значит нужен какой-то backing store и в Qt для этого есть, вроде как, Q_PROPERTY.
Сам диалог мы берем и рисуем за 5 минут (или больше, если 5 табов) в Qt Designer. И нам даже породят код для его генерации (главное в этот код не смотреть!) и будут этот код апдейтить если мы что-то передизайнили. Или даже возьмем QUILoader и код не нужен, нарисуем форму динамически, взяв ui-файл из ресурсов.
А вот дальше начинается сущее мучение:
- Для каждого элемента надо написать Q_PROPERTY(..) (можно и без нее, но сохранение-чтение с пропертями проще).
- Для каждой проперти - getter/setter ну и саму переменную для стораджа.
- Значения надо проинициализировать (в конструкторе). Готового механизма "списком" (key-value) нет. Ну, есть полуготовый, можно QMap<QVariant>, но тоже ничего хорошего.
- Нужно элемент диалога инициализировать из проперти перед показом диалога; нужно значение проперти обновлять, если в диалоге было нажато ОК.
- Нужен какой-то механизм сохранения значений/восстановления (внешнее хранение настроек). Опять, детали механизма есть, а готового - нет.
Или не ручками, скажите мне, что я пропустил что-то в Qt!!!
Нет, я понимаю, что цивилизованный мир сажает на эту работу индуса (а нецивилизованный - студента), тот легко напишет 75 геттеров-сеттеров методом Copy-Paste, по 750 штук в день. Но ведь не может быть, чтобы это место не было уже автоматизировано в Qt?
Comments
perl?
perl?
Поясни подробнее?
Поясни подробнее?
программой на перле генерировать необходимый код
программой на перле генерировать необходимый код
Индус на перле, понимаю! Можно наверное и C-шным препроцессо
Индус на перле, понимаю!
Можно наверное и C-шным препроцессором обойтись по меньшей мере для половины задачи.
Но меня удивляет другое: для всех типовых задач в Qt уже есть готовые решения (так было по сей день). Если решения нет, то значит задача нетиповая, а я ломлюсь напролом через стену в метре от ворот.
Мне, вот, пришлось вручную
Мне, вот, пришлось вручную сделать базовый класс для всех окон с настройками, перехватывать сигналы и по имени объекта и типу контрола вынимать значение и класть в настройки
Выкрутиться можно:
Выкрутиться можно: геттеры-сеттеры препроцессором, все остальное - тоже более-менее можно вроде бы. Естесвенно, accept/reject перехватить.
Меня удивляет, что в этом месте нет готового решения, типовая же задача.
Вроде бы с геттерами-сеттерами это место подперто в Qt Creator, надо посмотреть повнимательнее..
В MFC, помнится, было то же самое...
В MFC, помнится, было то же самое...
То же самое - это "все ручками"? Ну ерунда же какая-то, тип
То же самое - это "все ручками"?
Ну ерунда же какая-то, типовая же задача.
Можно мышкой натыкать. Тогда и переменная, и в конструкторе
Можно мышкой натыкать. Тогда и переменная, и в конструкторе будет сразу, отмена, но это не всегда удобно, чаще руками лучше.
Вообще в Qt по мере развития, некоторые вещи имели свойство усложняться (в сторону лишнего гемора). Тут наверное надо привыкать..
А может это в грид какой-нить запихать и шут бы с ним? Есл
А может это в грид какой-нить запихать и шут бы с ним?
Если честно, меня как пользователя 75 с гаком параметров править через ГУИ (да еще и разнесенные по разным вкладкам) - этож паника, анархия и технофашизм в одном флаконе. С этой точки зрения даже конфиг в XML смотрится уже неплохой идеей. Но тут, понятно, каждый сам себе злой чебурах.
75 - это для примера. У меня крик души настал на втором деся
75 - это для примера. У меня крик души настал на втором десятке, при том что 4 однотипных я сделал таки массивом.
Но вот у фотошопа (сложная программа) или там acdsee или, чтобы не про графику, у Foxit reader (посмотрел программы, окна которых открыты) - их больше, чем по 75 у каждой программы.
И ничего - у фотошопа я правлю единичные настройки, у acdsee - только quick view выключаю, Foxit reader - никогда ничего не правил вообще :)
Но при этом - найдется 1 или 5% пользователей, которые таки что-то поправят (и все - разное), поэтому параметры таки нужны.
Ну понятно. А в сторону QML, кторое якобы из себя все деклар
Ну понятно. А в сторону QML, кторое якобы из себя все декларативное, не смотрели?
Как я уже написал, решил не заморачиваться ради одного диало
Как я уже написал, решил не заморачиваться ради одного диалога.
Но посмотрю со временем, выглядит вкусно.
Сделать как в firefox about:config
Сделать как в firefox about:config
У FF для основных настроек есть человечий интерфейс... :)
У FF для основных настроек есть человечий интерфейс... :)
Ну вот и сделай для десятка настроек - человечий, и все оста
Ну вот и сделай для десятка настроек - человечий, и все остальное в advanced, в grid.
У нас есть система с ~10 тыс настроек.
Взвод индусов сажать?
Да, я кстати, подобное делал на метаописании. Т.е. есть XML-
Да, я кстати, подобное делал на метаописании. Т.е. есть XML-ник с перечислением виджетов, по ним в рантайме создаются объекты (классов же много меньше, правда). UILoader делает тоже самое, в принципе, но там надо руками двигать/привязывать, а это не всегда удобно. Да и компоненты кастомные были большей частью, с заточкой на тачскрин.
http://translate.google.com/translate?sl=ru&tl=en&js=n&prev=
http://translate.google.com/translate?sl=ru&tl=en&js=n&prev=_t&hl=en&ie=... >>> irc://freenode/#qt-creator
или
http://alextutubalin.livejournal.com/277551.html#comments >>> irc://freenode/#qt-ru
Может там кто чего скажет?
Мне казалось что у них появился какой-то механизм взять это
Мне казалось что у них появился какой-то механизм взять это из XML-я, и запихнуть туда же
Если нет написать один раз и забыть
КОгда мы написали целую морду биллинга на Qt (и несколько отдельных АРМов на её базе), то да, пришлось по мере использования дописывать под себя кирпичи
Описание формы - да. Всякий backing store - я не нашел. Наше
Описание формы - да. Всякий backing store - я не нашел. Нашел feature request для Qt creator, дескать неплохо бы для Q_PROPERTY порождать готовый код. И вроде даже сделали, но креатором я не пользуюсь.
Там есть новый механизм, который Qt Quick (QtDeclarative): описание на некоем мета-языке, похожем на JSON (а может это он и есть) и яваскриптом посыпано сверху. И да, это хорошая идея т.к. из плюсов все видно, но ради одного диалога Preferences я не стал затеваться с этим пока.
Понятно. Спасибо за информацию, а то я эту тему года четыре
Понятно. Спасибо за информацию, а то я эту тему года четыре уже не крутил, и никак не найду повода покрутить :)
Мы правда, тогда даже дизайнером не пользовались, кроме как для макетирования, чисто самому в коде писать получалось быстрее и компактнее, благо быстро своих оберток понарисовали для списков-селекторов и т.д.
Ну я тоже дизайнером не пользовался до сих пор - а тут решил
Ну я тоже дизайнером не пользовался до сих пор - а тут решил, что будет удобно т.к. диалог Preferences имеет свойство постепенно расти.
Пока не въехал, стоит ли оно того. Но интересно же.
Кстати, а Dynamic Property не то, что нужно?
Кстати, а Dynamic Property не то, что нужно?
В смысле - без геттеров-сеттеров, default - в гуе, а остальн
В смысле - без геттеров-сеттеров, default - в гуе, а остальное - так же? Ну да,наверное.
QMap - тоже вариант (и будут нормальные operator =)
Там есть Dynamic Property, которые вообще не объявляются. Он
Там есть Dynamic Property, которые вообще не объявляются. Оно создаётся во время QObject::setProperty и чтение только через QObject:property().
Если нужна реакция на изменение, то на объекте генерится QEvent::DynamicPropertyChange .
http://svn.webkit.org/repository/webkit/trunk/Source/WebKit/qt/Api/qwebp...
dynamicPropertyChangeEvent()
Я не вижу принципиальной разницы с QMap<Qstring,QVariant>.
Я не вижу принципиальной разницы с QMap.
Мой вопрос был в другом: казалось бы задача (диалог с кнопками + переменные связанные с кнопками и обновляемые по OK + сохранение в registry/whatever) - абсолютно типовая. Почему нет готового решения?
> Я не вижу принципиальной разницы с QMap<Qstring,QVariant>.
> Я не вижу принципиальной разницы с QMap.
Дык, в QObject оно уже есть. Зачем ещё? ;)
> Мой вопрос был в другом: казалось бы задача (диалог с кнопками + переменные связанные с кнопками и обновляемые по OK + сохранение в registry/whatever) - абсолютно типовая.
> Почему нет готового решения?
А ХЗ.
Я когда в последний раз диалог рисовал делал это крейтором. Оно мне полей понагенерило. Может это и есть предлагаемое решение.
1. зачем backing store? брать
1. зачем backing store? брать из виджетов по нажатию OK
2. я в qt designer вижу кнопки promote to.... и потом можно добавлять проперти, правда почему-то только строки или bool. немного через зад, но в принципе похоже на то что надо.
1) А по нажатию Cancel?
1) А по нажатию Cancel? Восстанавливать виджеты из сохраненной копии? Ну да, наверное можно, хотя не нравится.
2) Добавлять проперти можно и так (к кнопке/чекбоксу/whatever), но что толку то? Там будут некие константы, связать их со значениями в виджете только по нажатию ОК - да, можно в базовом классе, но опять вручную....
По нажатию cancel не делать
По нажатию cancel не делать ничего.
Ещё раз повторю всё: новые значения лежат в виджетах, если диалог accepted() (нажата OK) или нажата кнопка Apply - достаются оттуда, применяются и сохраняются в конфиге. Если rejected() (нажата cancel) - просто не делать ничего.
С пропертями я хорошего способа не знаю, просто натыкал в designer. Сам когда надо делаю руками да.
Ещё можно поиграть с моделями, иногда даже не табличные данные хорошо в них ложатся. А уж если есть однообразные данные - тем более.
Кроме конфига есть еще
Кроме конфига есть еще собственно сама программа, которая пользуется этими значениями на рантайме (например, если открыли новый файл, надо сходить в значения preferences и посмотреть, что там показывать).
Она может брать эти значения
а) из контролов (control->value() или control->isChecked())
б) из каких-то переменных в программе.
Вот второй случай - это и есть backing store. А первый - нехорош тем, что cancel мы нажали, а значения контролов от этого - не изменились.
а, это конечно да, надо
а, это конечно да, надо хранить.
а зачем вообще qt проперти, почему нельзя это обычными плюсовыми полями делать?
Чтобы поэкономить на
Чтобы поэкономить на сериализации, вместо перечисления всех полей в коде - цикл.
При этом сами пропертя могут быть и плюсовыми полями, конечно
Я налабал live templates в
Я налабал live templates в qtcreator, в три щелчка добавлялись пропертисы.
Отвлечённо (но чуть в тему) -- а никаким IDE не пользуешься?
Отвлечённо (но чуть в тему) -- а никаким IDE не пользуешься?
Я Intellij Idea юзаю, и оно умеет автоматом клепать геттеры/сеттеры (и реорганизацию, если что, одной кнопкой можно делать).
Visual C++ 2010 и тамошняя IDE. +Visual Assist X. А рефакт
Visual C++ 2010 и тамошняя IDE. +Visual Assist X.
А рефакторинга по одной кнопке я боюсь.
Я обычно перед началом использования всякого UIшного инструм
Я обычно перед началом использования всякого UIшного инструмента делаю себе примочки для этих целей. Т.е. типичное решение - табличка, UIшный код, адрес переменной - куда класть и всякие флаши (формат и т.д.)
На удивление, для многих это
На удивление, для многих это не такая типовая задача.
В смысле многие индусы(не только по национальности, но и моральные (я видел таких русских) как вы их тут называете) даже не задумываются о том, что большую часть их говнокода можно генерировать автоматом, точнее даже не генерировать в смысле статически/препроцессором/кодогенератором/метапрограммированием, а хотя бы большую часть реализовать динамически, в рантайме.
Например мне недавно пришлось убеждать одного морального индуса в том, что большую часть наговнокоженного кода для считывания кучи типовых значений из файла специального формата(есть библиотека, но не суть), можно выкинуть в трубу.
Но вы представьте, человек с десятилетним стажем C++ спрашивает - (суть, не дословно) "а как ре-использовать, ведь имена переменных разные". Он даже сначала не сразу понял, когда я ему показал как можно std::map использовать для "динамического создания" переменных.
А вы говорите "Для каждого элемента надо написать Q_PROPERTY(..)". Там блядь для сотен значений, с от силы с четырьмя разными типами, для каждого был написан, нет даже скопипастен метод с изменением имени переменной... А ведь эти методы ещё надо вызывать, и что вы думаете? сотня "if(name==v1){}else" - это блядь 21 век!
Статическое написание сотен
Статическое написание сотен копипастов имеет то преимущество, что имена переменных (методов и прочего) проверяются на этапе компиляции.
А динамически будет в одном месте map.set("var1",value), а в другом - map.get("var1_with_mistype"), особенно это весело, если используется расширенный get с default value.
Ну то есть понятно как лечить, ключ должен быть enum или чем-то подобным, но тоже полностью не спасет.
Ну и реальность в Qt похоже (не пробовал, но похоже), такая, что QtDeclarative - и есть ответ.
Да, на счёт текстовых имён
Да, на счёт текстовых имён переменных я тоже думал, и тоже пришёл к варианту с enum'ом, как раз из-за возможности уползания ошибок из компиляции в рантайм.
Кстати, из-за той копирастии методов, была не одна ошибка - то есть всё типично, скопоровали но не всё поменяли, что и следовало ожидать..
Ну да, или enum или дефайн с
Ну да, или enum или дефайн с кавычками(а ключ к мэпу - всегда без кавычек).
Но как-то это все безобразно скучно и муторно.
Копирастию методов легко сделать через дефайн, где и имя метода и имя переменной - все будет браться из одного слова, отчего проблем будет меньше.
Но проблемы на использовании никуда не денутся, если кто-то написал getVar7 вместо getVar17, то эту багу хрен поймаешь.
Копирастию методов легко
Копирастию методов легко сделать через дефайн, где и имя метода и имя переменной - все будет браться из одного слова, отчего проблем будет меньше.
там не всё так просто было - одним дефайном в том конкретном случае не отделаться - их нужно было много, так как всё было жутко размазано. В итоге чтобы добавить новый параметр (обычного типа) в формат файла (то есть в модуль считывания), нужно было изменить больше 10 (!) мест.
Но проблемы на использовании никуда не денутся, если кто-то написал getVar7 вместо getVar17, то эту багу хрен поймаешь.
До рефакторинга такая проблема могла возникнуть внутри модуля считывания файла (ну и, конечно, снаружи) - так как надо было вручную добавлять элемент в длинющую "if(){}else" и ещё в кучу мест.
А после рефакторинга проблема такого рода может возникнуть только вне модуля считывания файла (при использовании считанных данных). да и нет там таких имён..
Вообще я не против статических вариантов, только они должны не руками копироваться, а генерироваться либо компилятором, либо препроцессором, либо внешнем кодогенератором - и использоваться адектватно (то есть с пониманием того, что быстрее будет для реализации, и грамотной оценкой преимуществ и недостатков) (да, я кэп).
Я в рантайме использовал
Я в рантайме использовал setProperty. Кода в классе для свойства не было.
http://doc.qt.nokia.com/4.7-snapshot/qobject.html#setProperty
Лол. Используйте QSettings и
Лол. Используйте QSettings и будет вам счастье
Причем тут это? The QSettings
Причем тут это?
The QSettings class provides persistent platform-independent application settings.
А я вот спрашиваю про
Ну и дальше там.
Как вот QSettings помогут поставить Defaults по кнопке 'Reset Defaults'?