Название: Архитектура модели с параметрами различных типов Отправлено: __Heaven__ от Сентябрь 14, 2018, 09:47 Привет, друзья!
Прошу подсказать. Мне необходимо реализовать модель таблицы с настройками, в которой задаются параметры типа double, QString и unsigned Как бы так организовать хранение, чтобы в data() возвращался нужный параметр и в setData() он правильно конвертировался и складывался в структуру. Пока на ум приходит структура типа Код Пугают касты по смещению и раздутая реализация Название: Re: Архитектура модели с параметрами различных типов Отправлено: Igors от Сентябрь 14, 2018, 11:13 Аргумент setData() и возвращаемое значение data() - однозначно QVariant или его аналог (напр std::any). А вот как хранить - зависит от того сколько кода завязано на саму структуру. Может разумно начать с лобового QVariant (вместо struct row) и посмотреть сколько разборок вылезет. Если "достаточно много" - увы, придется городить темплейтщину.
Название: Re: Архитектура модели с параметрами различных типов Отправлено: ViTech от Сентябрь 14, 2018, 11:37 Мне необходимо реализовать модель таблицы с настройками, в которой задаются параметры типа double, QString и unsigned Как бы так организовать хранение, чтобы в data() возвращался нужный параметр и в setData() он правильно конвертировался и складывался в структуру. Хорошо бы привести побольше подробностей, желательно с примером. В каком виде существуют исходные данные, которые отображаются через модель? Что значит: "в setData() он правильно конвертировался и складывался в структуру"? Название: Re: Архитектура модели с параметрами различных типов Отправлено: Fregloin от Сентябрь 14, 2018, 14:14 Здесь явно через QVariant будет проще всего. Да и не так он много жрёт памяти.
Название: Re: Архитектура модели с параметрами различных типов Отправлено: __Heaven__ от Сентябрь 14, 2018, 19:45 Что-то подобное хочется иметь
Название: Re: Архитектура модели с параметрами различных типов Отправлено: __Heaven__ от Сентябрь 14, 2018, 19:54 Хорошо бы привести побольше подробностей, желательно с примером. В каком виде существуют исходные данные, которые отображаются через модель? Что значит: "в setData() он правильно конвертировался и складывался в структуру"? Исходные данные собираюсь записывать/читать в QSettings, который будет работать с ini или реестром в пределах моей программы. Сейчас создал структуру для передачи параметров в различные модули вида Код Параметры будут записываться в текстовый файл, который будут использовать программы расчёта. Формат файла нестандартный, когда-то давно придуманный. В setData хотелось бы, чтобы вызывался правильный метод toDouble/toString.... Список параметров в будущем может пополниться, убавиться, перетасоваться. Не хотелось бы в связи с этим делать много правок. Название: Re: Архитектура модели с параметрами различных типов Отправлено: __Heaven__ от Сентябрь 14, 2018, 20:03 Хотелось бы ещё, чтобы делегаты не позволяли в int записывать строку и double. Вроде, это тип QVariant определяет, если я не ошибаюсь. Сам проверять не хотел бы.
Название: Re: Архитектура модели с параметрами различных типов Отправлено: RedDog от Сентябрь 14, 2018, 20:41 Что-то подобное хочется иметь Подобное реализовали у себя в проекте QVariant-ом. Просто, удобно, заодно он и тип еще хранит.Название: Re: Архитектура модели с параметрами различных типов Отправлено: Igors от Сентябрь 15, 2018, 04:29 Тогда напрашивается QVariantMap (с ключами типа "Температура сплава").
Название: Re: Архитектура модели с параметрами различных типов Отправлено: ViTech от Сентябрь 15, 2018, 15:29 Исходные данные собираюсь записывать/читать в QSettings, который будет работать с ini или реестром в пределах моей программы. Сейчас создал структуру для передачи параметров в различные модули вида Код Параметры будут записываться в текстовый файл, который будут использовать программы расчёта. Формат файла нестандартный, когда-то давно придуманный. В setData хотелось бы, чтобы вызывался правильный метод toDouble/toString.... Список параметров в будущем может пополниться, убавиться, перетасоваться. Не хотелось бы в связи с этим делать много правок. Структуры данных должны в первую очередь быть удобными для использования в основной части программы, а не подстраиваться в угоду QSettings, QAbstractItemModel и прочим. Соответственно, если модулям удобно работать со структурой типа Parameters, значит такой она и должна быть. Если же это маленькая утилитка, которая что-то читает из QSettings, отображает/редактирует в QAbstractItemModel/QAbstractItemView и сохраняет в текстовый файл, то можно подумать о структуре с QVariant'ами. И я бы с опаской относился к конструкциям типа "void MyData::* fieldOffset;". QAbstractItemModel может напрямую работать с Parameters. Помимо самой структуры Parameters нужна дополнительная информация для неё. Это имена ключей для чтения/записи в QSettings, строки для "Название" в таблице. Их можно хранить в виде статичного списка строк в каком-нибудь контейнере. Насколько я понял, в таблице есть "разделители", значит поля Parameters не отображаются "один-в-один" в строки таблицы. Чтобы строкам таблицы дать какие-то осмысленные "имена", можно для этого завести enum {TemperatureXxxx, TemperatureYyyy, TemperatureSeparator, ResultFile, ...}. Этот enum будет находиться в соответствии со списком строк "Название" для таблицы и по нему можно делать switch по index.row() в data()/setData() для чтения/записи соответствующего поля структуры Parameters. Да, при изменении списка параметров многое придётся исправлять руками (равно как и для структур типа row из первого поста). Чтобы было больше автоматизации, нужно думать, как по другому можно представить список параметров (например, как элементы в контейнере или в tuple запихнуть) и как они будут отображаться в строки таблицы (один маленький частный вариант использования). Не факт, что там возни будет меньше. И нужно ли это будет остальным частям программы. Для варианта с использованием структуры типа row, можно попробовать привлечь std::function: Код
Что приведёт примерно к такому коду (возможно что-то можно ещё "укоротить"): Код
Название: Re: Архитектура модели с параметрами различных типов Отправлено: Igors от Сентябрь 15, 2018, 16:07 Структуры данных должны в первую очередь быть удобными для использования в основной части программы, а не подстраиваться в угоду QSettings, QAbstractItemModel и прочим. Соответственно, если модулям удобно работать со структурой типа Parameters, значит такой она и должна быть. C "концепцией" согласен, тем более что в расчетах QVariant скорее всего окажется невыносим. Но реализация монструозна :) Думается можно обойтись куда более простыми средствами, напр чем плоха простая перегонка из структуры в контейнер QVariant и обратно?Код
Название: Re: Архитектура модели с параметрами различных типов Отправлено: ViTech от Сентябрь 15, 2018, 17:10 C "концепцией" согласен, тем более что в расчетах QVariant скорее всего окажется невыносим. Но реализация монструозна :) Думается можно обойтись куда более простыми средствами, напр чем плоха простая перегонка из структуры в контейнер QVariant и обратно? Код
Можно и так. Тут получается, что если за исходную точку брать структуру типа Parameters, то большинство решений будет примерно "то же самое, только с боку" :). Так или иначе придётся к каждому полю структуры обращаться отдельно. Пока в С++ не добавят рефлексию, чтобы по полям итерировать как по tuple хотя бы. Ну или особо тёмную магию применять. Опять же, нужно дополнительную информацию где-то хранить ("Название") и как-то задавать соответствие между полем структуры и номером строки в таблице. В моих вариантах решения можно точечно изменять отдельное поле структуры (в setData() например) и, допустим, посылать сигнал, что оно одно изменилось, если такое потребуется. В Вашем решении структура изменится только целиком. Может такое поведение и лучше подходит, это уже поставленной задачи зависит. Название: Re: Архитектура модели с параметрами различных типов Отправлено: Igors от Сентябрь 16, 2018, 08:41 Можно и так. Тут получается, что если за исходную точку брать структуру типа Parameters, то большинство решений будет примерно "то же самое, только с боку" :). Так или иначе придётся к каждому полю структуры обращаться отдельно. Да, по имени/ключу (довольно популярная задача)Пока в С++ не добавят рефлексию, чтобы по полям итерировать как по tuple хотя бы. Ну или особо тёмную магию применять. Опять же, нужно дополнительную информацию где-то хранить ("Название") и как-то задавать соответствие между полем структуры и номером строки в таблице. Ладно, давайте еще "загрубим"Код Да, это просто "код начинающего". Ни std::function, ни лямбд, ни даже мапы :) Но если подходить непредвзято - а чем он собсно плох? Что мы выигрываем наворачивая "рефлексию" и.т.п.? Ну разве что "применяем на практике прочитанные знания", но объективно - аж ничего :) Название: Re: Архитектура модели с параметрами различных типов Отправлено: ViTech от Сентябрь 16, 2018, 13:57 Да, это просто "код начинающего". Ни std::function, ни лямбд, ни даже мапы :) Но если подходить непредвзято - а чем он собсно плох? Что мы выигрываем наворачивая "рефлексию" и.т.п.? Ну разве что "применяем на практике прочитанные знания", но объективно - аж ничего :) Так тоже можно. Если процессор занять больше нечем. А то им сейчас скучно на своих гигагерцах, пущай хоть строки посравнивают :). Плюс варианта с массивом ParameterRows в том, что там всё в одном месте собрано ("Название", чтение/запись поля, соответствие строке таблицы), а не разбросано по разным местам. При изменении Parameters проще исправлять и меньше шансов ошибиться/забыть. Массив формируется в compile time и инициализируется один раз во время запуска программы. И там ещё есть места для оптимизаций, это начальный вариант, что первое в голову пришло. Название: Re: Архитектура модели с параметрами различных типов Отправлено: sergek от Сентябрь 16, 2018, 18:56 Вариант, предложенный ViTech, мне нравится. Но для него нужно иметь шпаргалку "0 -Temperature X", "1 - Temperature Y" и т.д., 50 штук. Вполне себе рабочий вариант. Тем более, что формально расширять это применение довольно просто. Но все ж я таким шифровальным таблицам предпочитаю правила, которые проще запомнить и использовать.
Коллега, задававший вопрос, не сообщил главного - какой он хочет получить интерфейс. Если я правильно его понял, то запись и получение значений таблицы с настройками предполагается выполнять с использованием наименований параметров, исполняющих роль строковых ключей. Т.е. что-то типа setData(const QString& name, QVariant value) и QVariant data(const QString& name). Поэтому вариант словаря типа мапы здесь мне кажется наиболее уместным. Вопрос только в том, как это организовать, чтобы использование было наиболее простым. Вопрос эффективности оставим на потом, т.к. обращение к мапе занимает значительно меньшее время, нежели файловые функции. Я предлагаю сделать это так: 1) параметры, подлежащие хранению в файле, организовать в виде структуры (класса), аналогичной предложенной здесь Parameters; 2) создать базовый класс, например, CInterface, содержащий упомянутый словарь. Этот словарь в качестве ключа использует имя параметра, а содержит адрес (void*) этого параметра в структуре Parameters и его тип. В этом классе должны быть реализованы 3 метода: - setData, который по имени отыскивает нужный адрес параметра, выполняет требуемые преобразования типа и записывает в параметр значение; - data, который аналогично ищет, преобразовывает и возвращает значение в QVariant; - темплейтный метод push, который инициализирует упомянутый словарь (т.е. определяет тип и добавляет в мапу адрес параметра и его тип). 3) структура Parameters наследует этот базовый класс и имеет конструктор, в котором вызывается метод push со всеми параметрами. Все методы базового класса должны быть реализованы для всех возможных используемых в качестве параметров типов (или для всех типов в QVariant). Поэтому при использовании различных структур Parameters (с разными параметрами) следует только определить конструкторы. Добавил параметр - нужно добавить новый push. Реализация темплейта push примерно такая: Код: template<class C> void CInterface::push(C& ptr, const QString& name){ Код
Думаю, реализация data и setData не вызовет вопросов. Подход не теоретический, у меня работает давно. Возможно, с использованием c++11 можно сделать красиво, но я не трогаю - работает и ладно. Название: Re: Архитектура модели с параметрами различных типов Отправлено: Old от Сентябрь 16, 2018, 21:04 Вариант, предложенный ViTech, мне нравится. Но для него нужно иметь шпаргалку "0 -Temperature X", "1 - Temperature Y" и т.д., 50 штук. Так ViTech сразу предложил описать enum и никакие шпаргалки ненужны, а IDE еще будет автодополнять эти идентификаторы при наборе. - темплейтный метод push, который инициализирует упомянутый словарь (т.е. определяет тип и добавляет в мапу адрес параметра и его тип). А как вы в мапе тип сохраняете, в type_info?Название: Re: Архитектура модели с параметрами различных типов Отправлено: sergek от Сентябрь 16, 2018, 21:05 Не удержался, попробовал сделать пример. Оказалось, что темплейтный метод очень простой:
Код Весь пример - в прикрепленном файле. Название: Re: Архитектура модели с параметрами различных типов Отправлено: sergek от Сентябрь 16, 2018, 21:36 А как вы в мапе тип сохраняете, в type_info? QVariant::type()Название: Re: Архитектура модели с параметрами различных типов Отправлено: Igors от Сентябрь 17, 2018, 07:04 Весь пример - в прикрепленном файле. Все-таки цеплять наследованием не очень хорошо - структура может быть вообще "незнакома с Qt" :) Но в целом мне понравилось, спасибоВариант, предложенный ViTech, мне нравится. Но для него нужно иметь шпаргалку "0 -Temperature X", "1 - Temperature Y" и т.д., 50 штук. Да, нужны enum'чики, по сути мы присваиваем каждому полю уникальное ID. Пусть его придется мапить в имя и обратно, но часто это имеет смысл, обращение по ID удобнее, Ну и опять-таки, можно обойтись простейшими средствами, напрКод Оформлять это в более общем виде (неизбежно раздувая, наворачивая код) - ну не знаю.. "Выйгрышь" конечно будет (если такие структуры идут бурным потоком), но насколько - хз. Спрячусь за стандартную фразу "зависит от задачи" :) Название: Re: Архитектура модели с параметрами различных типов Отправлено: sergek от Сентябрь 17, 2018, 09:34 Да, нужны enum'чики, по сути мы присваиваем каждому полю уникальное ID. Пусть его придется мапить в имя и обратно, но часто это имеет смысл, обращение по ID удобнее А пожалуйста... (c) "И". В CInterface меняем QMap на вектор, в push вместо parameters[name] используем append, забываем name и городим enum ;) Для красоты еще можно определить operator[].Вообще-то в оригинальном варианте у меня используется вектор, поскольку вся конструкция используется для сериализации/десериализации объектов классов при передаче по сети (есть у меня библиотека удаленных вызовов процедур). Поэтому разыменования членов класса мне не требуется. Причем, для простых типов вычисление размера тоже очень простое: QMetaType(addr.type).sizeOf(). Название: Re: Архитектура модели с параметрами различных типов Отправлено: __Heaven__ от Сентябрь 17, 2018, 20:27 Предложения с name, кажется, не подходят. Используется 2 язычный интерфейс.
Сейчас ещё рассматриваю вариант не дешевле ли будет договориться сделать всё на виджетах... Название: Re: Архитектура модели с параметрами различных типов Отправлено: __Heaven__ от Сентябрь 17, 2018, 20:31 Ой. Сообразил. name это же имя в настройках :)
Название: Re: Архитектура модели с параметрами различных типов Отправлено: Racheengel от Сентябрь 19, 2018, 18:39 У QVariantMap главный плюс - автоматический поиск по ключу, без свитчей и лямд.
Поэтому мы делали подобную штуку именно через варианты. Разве что надо обращать внимание на типы - у QVariant есть пересекающееся подмножество с QMetaInfo, а есть не пересекающееся (косяк Qt, мож когда починять...) |