Skip to content

Latest commit

 

History

History
162 lines (90 loc) · 21.3 KB

architecture_ru.md

File metadata and controls

162 lines (90 loc) · 21.3 KB

Приложение построено с применением библиотеки qt, его визуальный интерфейс написан при помощи языка qml (и в дальнейшем вся работа с GUI будет полностью реализована в нем), а бекэнд реализован на c++. Данные хранятся в виде специальной папки (см. раздел Хранение информации, в целом считается что почти весь интерфейс может быть построен динамически на основании файла struct.json из этой папки. Хотелось бы отметить что поскольку данный проект начал писаться еще во времена qt 4.7 и собрал немало костылей за время своего написания, некоторые моменты в описании касательно невозможности сделать те или иные вещи в qt могут быть устаревшими.

#backend

Все имеющиеся в базе таблицы можно разделить на два типа:

  • Независимая таблица, может показываться сама по себе, имеет переходы на следующий уровень, также может иметь разные представления для списка и для конкретной записи (например, в списке все признаки аверса сливаются в одну строку, а в конкретной записи показываются по отдельности). Также именно в ее ведении все управение подчиненными табличками Умеет: -выдавать модель для отображения -получать и выбирать по ид запись и представлять ее в виде json. В понятие записи входят также подчиненные модели -копировать запись включая подчиненные модели

  • Подчиненая таблица, например, ссылки на источники, не показываются отдельно от своей главной таблицы. Используются основной таблицей для получения списка и для внесения изменений. В данный момент не реализована и не уверен что будет, по факту это таблицы без использования UUID

Для управления их взаимодействием вводится понятие узла (ноды), который соотвествует определенной таблице, а также знает какие у нее подчиненные таблицы и куда можно с нее перейти

Бекэнд приложения состоит из следующих классов и модулей:

CBController - основной класс для взаимодействия между GUI и бекэндом, в qml представлен синглтоном с именем CBApi из пакета CB.api . Является мостиком между GUI и хранилищем данных\настройками\парсинг struct.json и т.п.

CBTranslator - модуль-локализатор, крайне прост и написан исключительно потому что родной QTranslator (наследником которого он является) работает с встроенными в программу на этапе компиляции переводами, а мне нужны были динамически подгружаемые из файла. Переводы грузит из json, причина - так было написать быстрее всего

CBAttachmentsProvider - провайдер вложений, на нем лежит подготовка данных для показа и модификация вложений, также он умеет открывать вложение во внешней предназначеной для этого программе

CBImageProvider - провайдер изображений, наследник QQuickImageProvider. Имеет тесную инетграцию с CBAttachmentsProvider, что необходимо чтобы знать откуда показывать картинки, если вложение не является картикнкой будет показана иконка файла. Если и с показом иконки не сложилось (обычно это связано с отсутсвием Main.jpg для записи), то выводится стандартная картинка с кругом и вопросительным знаком

CBBaseIconProvider - провайдер изображений, наследник QQuickImageProvider. Служит для показа иконки базы (по факту крупного изображения), или стандартной иконки базы (если в папке базы нет иконки), или иконки добавления новой базы (если там пункт добавить новую)

CBBaseProvider - основной класс по работе с БД, отвечает за подключение к конкретной базе

CBNode - описание узла данных, содержит имя таблицы, куда из нее можно перейти и некоторые данные для построения интерфейса.

CBSqlRelationalTableModel - класс для отображения модели в qml. На самом деле мне не нужен потомок QSqlRelationalTableModel, но при использовании QSqlTableModel у меня возникли проблемы - см. мой вопрос на stackoverflow. Также там есть пара нужных мне методов, таких как генерация json для отображения записи в qml и сортировки по названию столбца

CBUtils - небольшой модуль, служащий для сборки воедино всяких мелких утилит.

CBWordLCS и CBFieldDifference - служат для построчного сравнения двух записей, первое это собственно алгоритм сравнения (стандартный Longest common subsequence)б второе хранилище обработанных записей для отображения в qml

GUI

GUI выполнен при помощи языка qml. Основной компонент (main.qml) представляет собой окно из верхнего таббара и stackview, в который загружаются отображающие данные виды. Эти виды бывают двух типов:

  • ListView - отображает список записей при помощи CBSqlRelationalTableModel, не содержит возможности непосредственного редактирования (вставка и удаление осуществляется при помощи обращения к полной форме конкретной записи)

  • FullInformationViews - полная форма конкретной записи, строится автоматом на основе списка полей и их типов, может быть отредактирована. В качестве носителя данных выступает json-объект который генерится из записи классом CBNode и помимо полей собственно записи включает в себя еще и ссылающиеся на эту запись данные из подчиненных таблиц (не реализовано).

Виды генерятся динамически на основании данных из struct.json или прямо из данных о таблице если в struct.json ничего не определено. Также виды могут быть загружены из заранее подготовленного файла, что сделано для случая когда вид содержит нечто специфическое, что не хотелось бы реализовывать при помощи абстраций или дополнительных параметров, например, я хочу подсвечивать ячейку разновидности определенным цветом в зависимости от того что перед намим - обычная разновидность, фальшак, новодел и т.п. Виды называются так же как и представляющая их нода + List или Full и могут быть переделаны из полученных сгенерированных форм, которые, в свою очередь, будут выгружаться в файл если ключ needCollect в настройках задан как true

Также имеется некоторое количество служебных видов, реализованных в виде qml-файлов:

  • FilterDialog.qml - служебная форма для фильтрации

  • AttachmentList.qml и AttachmentFullInfo.qml - список вложений и полная информация о вложении соотвественно

  • BackgroundRect.qml - задник меняющий чередущющий свой цвет на четных и нечетных ячейках

  • LabeledTextInput.qml - текст + заглавие, также этот компонент отображает и гиперссылки за счет свойства linkable. Если оно равно true (а по умолчанию оно false), то в конце поля возникает кнопка-стрелка при нажатии на которую осущетвляется переход по ссылке

  • LabeledLongText.qml - текст в несколько строк + заглавие

  • LabeledComboBoxInput.qml - текстовое поле с выбором и заглавием

  • LabeledDateInput.qml - поле ввода с показом диалога выбора даты

  • NextLevelList.qml - список таблиц к которым можно перейти от текущей

  • FormCreator.js - генератор форм

  • BasesList.qml - форма для отображения списка известных баз и их загрузки

  • GUIStyle.qml - синглтон для разного централизованного отображения на мобильном устройстве и на десктопе

  • DiffView.qml - показывает разницу двух записей, по сути, визуализирует CBFieldDifference

  • LabeledIcon.qml - мелкая служебная qml для показа иконка + текст в меню

  • ru_RU.json - перевод основных строк на русский

Хранение информации

Хранение информации реализовано в виде папки на диске со следующей структурой:

├───attachments
│   └───19a6afd7-ab2c-4024-907f-5a340299e749
│   	├───attributes.json
│   	├───note1.pdf
│   	└───note2.doc
├───forms
├───languages
│   └───ru_RU.json
├───base.sqlite
├───about.html
├───icon.png
└───struct.json

attachments - папка с вложениями, вложения могут быть любыми и открываются во внешних программах. Назначение данной сущности - нечто наподобие заметок, например, статья как отличать монеты Елизаветы разных дворов друг от друга или скан заключения ГИМа. Любой записи в базе с UUID будет соотвествовать папка с именем в виде этого UUID, в которой будут собственна файлы-вложения и attributes.json содержащий описания этих вложений в произвольной форме (из жестко заданого только то что это массив и наличие поля file - имя файла). Раньше была еще отдельная независимая сущность картинок, но теперь она слита с вложениями. Еще из важного - в программе имеется несколько костыльное понятие главной картинки для записи у которой есть UUID, задача этой картинки - быстрое узнавание записи чисто визуально без вчитывания в поля. Задается эта главная запись чисто с помощью переименования картинки

forms - папка с файлами qml, в которых лежат подготовленные заранее формы

languages - папка с переводами. Не то чтобы я пытался замахнуться на продукт с международным использованием, просто проще всего сматчить названия полей в таблице на английском прямо в название полей в интерфейсе чем делать отдельно подписи и следить чтобы ничего не перепуталось.

base.sqlite - основное хранилище данных, база sqlite

struct.json - заданет связи таблиц друг с другом и некоторые другие нужные для ГУЯ вещи (откуда брать данные для комбобоксов, что выводить в списке и т.п.). Причина появления - хотелось менять некоторые параметры без перекомпиляции программы

about.html - описание таблицы для диалога about, причина появления - хотелось где-то сохранить откуда я надергал данных для базы + возможность прямо из программы посмотреть что это за версия базы. В данный момент закомментировано из-за проблем при запуске на Android, на котором нет QtWebKit

icon.png - иконка базы, именно ее показывает CBBaseIconProvider. Может и не быть - тогда будет показана иконка по умолчанию

#struct.json

struct.json - это файл конфигурации конкретной базы данных, который задет внешний вид таблиц, записей и переходы между ними.

В корне находятся следующие поля:

  • name - название БД, которое будет отображаться в различных местах.

  • startTable - имя ноды с которой будет начато отображение

  • nodes - массив нод, структуру элемента подробнее смотри ниже

##Описание ноды в struct.json

Описание ноды включает в себя следующие поля:

  • name - имя ноды, используется как идентификатор

  • usesUUIDs - флаг нужно ли генерить поле id с ууидом при вставке новой записи, если он не определен поле будет генериться. По сути это отличие главной таблицы от подчиненной

  • sortColumn - поле по которому будет сортироваться таблица при выборке

  • listViewFields - массив полей отображающиеся в списке, в данный момент считается что все поля чисто текстовые

  • childNode - массив дочерних нод, описывается в виде имя ноды и как названо поле id в дочерней ноде (оно там является внешним ключем)

  • subNodes - массив сабнот, полностью идентичен по описанию childNode, отличается только отображение. В данный момент не задействовано и скорее всего когда буду (если буду) задействовать солью их с childNodes путем добавления поля isSubnode в элементы массива

  • fullFormFields - список полей, отображаемых в полном виде записи. Структура элемента - имя поля из таблицы, тип поля и необходимые для отображения данные или же просто строка-название поля (в этом случае отображение аналогично просто тексту из списка ниже). В данный момент есть следующие типы полей:

** просто текст - тип не указан или вместо него что-то из не перечисленного тут, поле будет выведено при помощи простого текстового поля с надписью

** combo - комбобокс, помимо имени и типа содержит еще одно из двух полей: *** query описывающее какой запрос нужно задействовать для получения списка значений в подстановку. *** list - список возможных значений, формат как у обычного js массива

** date - дата, выводится календарь для выбора данных, отображается как текстовое поле с ИСО датой

** hyperlink - по сути тот же простой текст, но с кнопкой перехода на ссылку

** long text - предназначено для длинных текстов

  • fullFormExceptedFields - обратное к fullFormFields, список строк-названий полей которые не должны быть включены в форму

  • filters - предопределенные фильтры, массив, объекты в котором представляют из себя пары name-condition. condition это сразу sql-условие, и изменить его из интерфейса на данный момент нельзя.

#Слияние записей

Данный процесс запускается из формы сравнения записей (DiffView.qml) т.к. именно в этой форме показываются данные о различиях записей и представляет собой последовательный перебор полей записей. Если их значение полей у обеих записей сопадает - то оно просто присваивается полю результата слияния, если же нет - показывается диалог с подсвеченой разницей где вручную надо будет поправить значение (по умолчанию воводится наибольшая общая последовательность). В конце необходимо задать разницу которая будет использоваться для пометки данных из второй записи. Непосредственно слияние означает перенос всех данных из подчиненных таблиц и вложений в первую запись, удаление второй записи и перезапись первой результирующими данными, при этом разница дописывается в поле comment из подчиненных таблиц (да, де факто такое поле всегда есть) и свойство description вложений связаных со второй записью