Russian Qt Forum

Программирование => Общий => Тема начата: Igors от Март 07, 2013, 18:52



Название: Import As Open
Отправлено: Igors от Март 07, 2013, 18:52
Добрый день

Приложение работает со "своими" файлами данных, конечно умеет их читать/писать. По существу в файлах хранятся сериализованные контейнеры объектов. Также приложение умеет импортировать данные из др (чужих) файлов таким образом

a) читаем чужой файл  в некоторый промежуточный формат

b) спрашиваем пользователя что нужно сделать (проверить данные объектов, разбить какие-то объекты на несколько и.т.п). Выполняем то что пользователь сказал, предъявляем ему отчет о проделанной работе

c) конвертируем промежуточные данные в родной формат, записываем файл на диск и загружаем

ЦЕЛЬ: выполнить все то же самое (процедуру импорта) но не для чужого, а родного файла. Неожиданно это оказывается не легче а труднее. Беда в том что "промежуточный формат" содержит раз в 10 меньше данных чем родной. Это естественно т.к. подходящих данных в чужих файлах немного, только самые базовые. Идя по пути выше (так и сделано сейчас) пользователь теряет 90% родных данных - ну и законно недоволен.

Ваше мнение?

Спасибо


Название: Re: Import As Open
Отправлено: Old от Март 07, 2013, 18:59
Ваше мнение?
Хреново. :)


Название: Re: Import As Open
Отправлено: Bepec от Март 07, 2013, 19:01
Вопрос глобальнее - а зачем ему импортировать, если он может читать спокойно?


Название: Re: Import As Open
Отправлено: Igors от Март 07, 2013, 19:17
Хреново. :)
Ценный и очень информативный ответ  :)

Вопрос глобальнее - а зачем ему импортировать, если он может читать спокойно?
Чтобы выполнить ф-ционал пункта b)


Название: Re: Import As Open
Отправлено: Old от Март 07, 2013, 19:20
Ценный и очень информативный ответ  :)
Честно говоря, я не понял, что вы хотели спросить. Поэтому пошутил. :)
Что тут можно сказать - нужно расширять промежуточный формат или делать отдельную процедуру экспорта для своего формата.


Название: Re: Import As Open
Отправлено: Igors от Март 07, 2013, 19:28
Что тут можно сказать - нужно расширять промежуточный формат или делать отдельную процедуру экспорта для своего формата.
Экспорт в свой формат есть (это просто сериализация) но к ней трудно присобачить ф-ционал импорта. Сериализация подразумевает что все сериализуемое в памяти, что ничего не изменится до ее окончания и.т.п. А импорт довольно свободно манипулирует с объектами, может создавать из одного несколько, удалять, менять иерархию.

Ну а расширять промежуточный - это месяцы чтобы получить гнусное копирование ф-ционала  :'(


Название: Re: Import As Open
Отправлено: Old от Март 07, 2013, 21:18
Ну а расширять промежуточный - это месяцы чтобы получить гнусное копирование ф-ционала  :'(
А откуда взялся промежуточный? Почему для загрузки не использовать "рабочие понятия (сущности)". Для чему нужны "промежуточные"?


Название: Re: Import As Open
Отправлено: ConConovaloff от Март 08, 2013, 05:56
Добрый день.

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

Если функционал промежуточного файла реализуется отдельным классом, то тут явные признаки для применения паттерна стратегия:
- пишем интерфейс для классов которые обрабатывают различные форматы.
- пишем класс для обработки своего формата и наследуем написанный выше интерфейс.

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


Если вам нужно добавить функционал вообще не трогая промежуточную часть и формат его файла, то вопрос в том, что-же именно вы можете изменить, чтобы работать с +90% функционала заложенного в вашем формате?


Название: Re: Import As Open
Отправлено: Igors от Март 08, 2013, 08:39
А откуда взялся промежуточный? Почему для загрузки не использовать "рабочие понятия (сущности)". Для чему нужны "промежуточные"?
Чужие файлы имеют другие данные. Напр чужой 3ds файл имеет для каждого полигона "индекс материала". Их нужно сохранить, потом (когда 3ds файл полностью прочитан) выпадет диалог, и пользователь может поставить бубочку "разделить по материалу" - и создастся неск нативных объектов с разными материалами т.к. у меня 1 материал на 1 объект. Поэтому обременять нативные данные посторонними - смысла никакого.

Вроде напрашивается использовать чтение родного. Но тогда загружаемые данные становятся "текущими", надо резолвить все связки между объектами и.т.п. (что мы не так давно обсуждали). А загрузив, да, могу записать, но не имею никакой свободы создания/манипуляции объектами - ведь они вмонтированы в текущую сцену




Название: Re: Import As Open
Отправлено: schmidt от Март 08, 2013, 10:13
Цитировать
b) спрашиваем пользователя что нужно сделать (проверить данные объектов, разбить какие-то объекты на несколько и.т.п). Выполняем то что пользователь сказал, предъявляем ему отчет о проделанной работе

А зачем вообще пользователю вашей программы нужно знать об ее устройстве - в частности, устройстве формата файла? Это же сплошные головняки - пользователь хочет просто открыть файл в программе, а она в ответ начинает засыпать его малопонятными диалоговыми окнами "для выбора формы импорта", вместо того, чтобы просто открыть файл и работать с ним.

Цитировать
c) конвертируем промежуточные данные в родной формат, записываем файл на диск и загружаем

А работа в оперативной памяти уже не в моде? :) Или у вас в программе имеется один большой C++ метод, который вы используете в любой ситуации? Разбейте свой код на несколько независимых методов и используйте этот базис для решения ваших задач без лишних ухищрений.

Нужно открыть файл? - метод readFile(). Программа имеет несколько форматов? Пусть везде по прежнему вызывается readFile() а его реализация включает в себя определение формата конкретного файла и вызов специфического метода readFileFormat_v12(), readFileFormat_v13(), и.т.д. readFile() определил что формат файла не родной? Пусть вызывает метод importFile(), который в свою очередь проверит, входит ли данный тип файла в список тех, что программа умеет импортировать, и произведет импорт. Если пользователь явно пожелает сохранить файл в родном формате - в этом случае пусть вызывается метод saveFile(), созраняющий рабочие данные программы из оперативной памяти на диск в "новейшем" формате.

Вот вам псевдокод:
Код:
class MainWindow {

readFile();
saveFile();
importFile();

_readFileFormat_v12();
_readFileFormat_v13();
_readFileFormat_v15();
}

// Реализация
MainWindow::readFile() {
// Выбираем файл для открытия
file = getOpenFileName();

// Получаем идентификатор определенной версии родного формата
mgnum = getFormatMagicNumber(file);

// Читаем данные из файла
switch(mgnum) {
case FORMAT_VERSION_12:
app_data = _readFileFormat_v12();
break;
case FORMAT_VERSION_13:
app_data = _readFileFormat_v13();
break;
case FORMAT_VERSION_15:
app_data = _readFileFormat_v15();
break;

// Если это не родной файл программы, пробуем импортировать
default:
app_data = importFile(file);
break;
}

// Возвращаем прочитанные данные
return app_data;
}

MainWindow::importFile(file) {
// Определяем формат стороннего файла и читаем его
external_format = getExternalFileFormat(file);
switch(external_format) {
case EXTERNAL_FORMAT_TXT:
imported_data = _readFileDataFromTxt(file);
break;
case EXTERNAL_FORMAT_XML:
imported_data = _readFileDataFromXml(file);
break;

// Если формат файла недоступен для импорта
default:
break;
}

// Возвращаем прочитанные данные
return imported_data;
}

MainWindow::saveFile() {
file = getSaveFileName();

_saveToNewestFileFormat(file);
}


Стремитесь к простоте и не утомляйте пользователя подробностями устройства своей программы, он это не оценит, независимо от уровня его технической подкованности :)


Название: Re: Import As Open
Отправлено: Igors от Март 08, 2013, 10:57
Стремитесь к простоте и не утомляйте пользователя подробностями устройства своей программы,
Какой Вы быстрый  :) Вот только желанная простота почему-то получается только на простых учебных примерах.

вместо того, чтобы просто открыть файл и работать с ним.
Да, родной файл открывается без всяких квешнзов. Он добавляется в проект и все объекты этого файла знают откуда они считаны. Можно добавить любое число файлов данных или удалить их из проекта, при этом все объекты этого файла будут автоматически удалены. Поэтому при импорте обязательно конвертировать в родной и потом его загружать. Данные только висящие в памяти (и нигде не сохраненные) не допускаются (и это правильно)

А зачем вообще пользователю вашей программы нужно знать об ее устройстве - в частности, устройстве формата файла? Это же сплошные головняки - пользователь хочет просто открыть файл в программе, а она в ответ начинает засыпать его малопонятными диалоговыми окнами "для выбора формы импорта", вместо того, чтобы просто открыть файл и работать с ним.
В любом приложении практически любой импорт имеет опции, по той простой причине что без них не обойтись. Будем пересчитывать нормали? Будем удалять невалидную геометрию? И.т.д. (приличный список). А просто так "ну вот загрузил с какими-то дефаултами" несерьезно   


Название: Re: Import As Open
Отправлено: Old от Март 08, 2013, 11:16
Данные только висящие в памяти (и нигде не сохраненные) не допускаются (и это правильно)
Что это значит?
А как пользователь начинает новый проект?
Или проблема в том, что программа написана так, что в любой момент времени может быть открыт только один проект?


Название: Re: Import As Open
Отправлено: Igors от Март 08, 2013, 11:41
Данные только висящие в памяти (и нигде не сохраненные) не допускаются (и это правильно)
Что это значит?
А как пользователь начинает новый проект?
Создается пустой проект и предлагается пользователю добавить файл(ы) данных.


Название: Re: Import As Open
Отправлено: schmidt от Март 08, 2013, 11:58
Цитировать
Создается пустой проект и предлагается пользователю добавить файл(ы) данных.

Снова перекладываете головную боль программиста на пользователя  :) Зачем ему знать о физической/логической структуре файла проекта? Ему с трехмерными моделями работать надо, а не бродить по диалоговым окнам программы :)

Цитировать
Какой Вы быстрый  Улыбающийся Вот только желанная простота почему-то получается только на простых учебных примерах.

Идеал на 100% недостижим, да, но на него тем не менее стоит ориентироваться :)

Цитировать
Данные только висящие в памяти (и нигде не сохраненные) не допускаются (и это правильно)

А как же несохраненные изменения в процессе работы с проектом? Или программа заставляет пользователя ходить по минному полю, насильно записывая любые изменения в файл на диске сразу?


Название: Re: Import As Open
Отправлено: Igors от Март 08, 2013, 12:45
Снова перекладываете головную боль программиста на пользователя  :) Зачем ему знать о физической/логической структуре файла проекта? Ему с трехмерными моделями работать надо, а не бродить по диалоговым окнам программы :)
Аналогия: проект в любом IDE С++. Выходит Вам с исходными текстами надо работать, а знать где они находятся на диске и как они сложены в фолдерах необязательно?  :)

А как же несохраненные изменения в процессе работы с проектом? Или программа заставляет пользователя ходить по минному полю, насильно записывая любые изменения в файл на диске сразу?
Изменения сохраняются в файле проекта, они не касаются моделей и нашей темы. Экспорт (сериализация) модели возможна только по запросу (относительно редкая операция). Если модель в использовании - ее файл не может быть перезаписан.


Название: Re: Import As Open
Отправлено: Bepec от Март 08, 2013, 14:11
Кхм, выражаю своё недоумение "пустым" проектом.

В любом IDE/редакторе мне даются исходные данные. Как то пустой холст/модель/данные. И да, необязательно знать где они на диске :D

И по поводу импорта - я так понимаю, вам необходим не импорт данных. А скорее вам необходимо провести операцию над данными в файле и уже результат получить. И это уже вопрос не сериализации и не хранения. Это уже повод для отдельной утилиты / класса.

Как говорится разделяйте суп и мух. Импорт данных - отдельно. Работа и преобразование данных - отдельно.


Название: Re: Import As Open
Отправлено: schmidt от Март 08, 2013, 17:35
Цитировать
Выходит Вам с исходными текстами надо работать, а знать где они находятся на диске и как они сложены в фолдерах необязательно?

Именно так :) Цель пользователя - работа с исходным кодом на каком-либо языке программирования, разработка будущего приложения. Работа с файлами на диске не является целью пользователя, большинство программ просто принуждают своих пользователей этим заниматься. Но то, что эта модель используется повсеместно, вовсе не означает, что она удачна. Просто с точки зрения разработки гораздо проще предположить, что пользователь знаком с устройством файловой системы и свалить ответственность на него, чем грамотно скрыть от него всю ненужную ему кухню :)

Большинство пользователей вряд ли будут настраивать параметры импорта каждый раз. Скорее всего они просто будут пропускать это назойливое диалоговое окно, не глядя. Так стоит ли заставлять человека просто лишний раз нажимать "Enter"? :) Программа должна просто работать, задавая минимум вопросов. Пусть она импортирует все возможные данные без потерь, дав опытному пользователю возможность впоследствии выбросить ненужное или изменить/пересчитать определенные данные. Пользователи тихо ненавидят, когда их допрашивают на каждом шаге посредством диалоговых окон :) Решать за пользователя, что ему нужно, а что нет, конечно, не стоит, но это не повод устраивать пользователю допрос на каждом шагу :)


Название: Re: Import As Open
Отправлено: Igors от Март 09, 2013, 09:35
Импорт данных - отдельно. Работа и преобразование данных - отдельно.
Именно так и сделано сейчас :) Проблема в том что ф-ционал импорта (диагностика, исправление ошибок, разбиение и др) оказывается необходимым и для нативных файлов данных.

Программа должна просто работать, задавая минимум вопросов.
Задает импорт вопросы или нет, плохо или хорошо пустое окно - какое отношение это имеет к теме? Понимаю всем хочется покалякать после трудового дня, это нормально. Но все-таки давайте ближе к делу. Спасибо за понимание.


Название: Re: Import As Open
Отправлено: Igors от Март 11, 2013, 09:32
Сделал изменив/дополнив сериализацию. Выглядит так: если задана "запись с проверкой", то

- часть сериализуемых данных копируется
- из этой копии создаются структуры  данных для ф-ционала импорта
- выполняется ф-ционал и результат конвертируется обратно в копию
- копия сериализуется

При этом создавать/удалять объекты я не могу, это для нативных файлов не поддерживается.
Впечатление элегантности это конечно не производит, но лучшего не нашел


Название: Re: Import As Open
Отправлено: schmidt от Март 11, 2013, 13:29
Цитировать
Но все-таки давайте ближе к делу.

Давайте :)

Цитировать
Проблема в том что ф-ционал импорта (диагностика, исправление ошибок, разбиение и др) оказывается необходимым и для нативных файлов данных.

Это говорит о том, что код/функционал, который требуется в нескольких местах стоит выделить в отдельные  независимые методы. Иными словами, если у вас есть солёный борщ и несоленая каша, лучшим вариантом будет взять чистую соль с полки и посолить кашу, чем вливать в нее борща :) Пусть у вас будет пара-тройка функций с именами а-ля _validateDataIntegrity(), _fixInternalRelations(), _splitPolygons() , которые вы будете использовать независимо как в процедуре открытия нативных файлов так и в процедуре импорта сторонних файлов. По-моему так будет проще и логичнее, чем тащить нативные файлы через чужеродную им процедуру импорта :)


Название: Re: Import As Open
Отправлено: Igors от Март 11, 2013, 16:46
Это говорит о том, что код/функционал, который требуется в нескольких местах стоит выделить в отдельные  независимые методы.
..
Пусть у вас будет пара-тройка функций с именами а-ля _validateDataIntegrity(), _fixInternalRelations(), _splitPolygons() , которые вы будете использовать независимо как в процедуре открытия нативных файлов так и в процедуре импорта сторонних файлов.
Ну если бы все было так просто (выделить неск ф-ций), то вряд ли у меня бы возникли проблемы  :)

По геометрии мой формат близок к тому что используется в OpenGL, том же Qt3D и во многих др местах. На рендере все выходит хорошо, но, к сожалению, он никак не подходит для каких-то операций перестройки модели, обнаружения ее дефектов и.т.п. Ну вот напр простейшая модель: кубик, 6 граней, 8 углов, какие могут быть проблемы? Дело в том что точек (вертексов) в нем совсем не 8, а 24. Пытаться что-то с такой моделью делать - обречено, т.к. все время будем натыкаться на проблему "а какая точка?". Приходится перегонять в др структуры данных, что тянет не одну тысячу строк. И.т.д.

Иными словами, если у вас есть солёный борщ и несоленая каша, лучшим вариантом будет взять чистую соль с полки и посолить кашу, чем вливать в нее борща
Давая такие простые-цветистые советы может стоит подумать - а человек действительно этого не знал (ну никак не мог догадаться)?. В общем, тлетворное влияние "вопросов новичков" :)


Название: Re: Import As Open
Отправлено: schmidt от Март 11, 2013, 22:17
Если я правильно понял, то у вас имеется в первую очередь "родной" формат, описывающий геометрические модели, к нему написаны функции сериализации/десериализации, в этом формате, собственно, и хранятся данные в native-файлах программы. Но беда его в том, что он плохо приспособлен для выполнения операций над моделями, такими как проверка на корректность, преобразования, расчеты, и.т.п. Для этого вы вводите в программу "оперативный" формат - создаете на основе native-формата дополнительные структуры данных, с которыми можно легко проводить различные операции. Это вполне логичныо :)

Цитировать
Беда в том что "промежуточный формат" содержит раз в 10 меньше данных чем родной.
...
пользователь теряет 90% родных данных - ну и законно недоволен.

Проблема, вероятно, в том, что вы безвозвратно отбрасываете 90% данных при переходе в "оперативный" формат. Что если реализовать функции преобразования модели из формата хранения в оперативный и обратно без потерь данных? Тогда вы сможете читать нативный файл с моделями, сериализованными в формата хранения, затем преобразовав их (без потерь) в оперативный формат, проверить на корректность, провести еще какие-то необходимые операции, а когда придет пора сохранить файл - снова преобразовать модели в "неповоротливый" формат хранения и сериализовать в файл. А импорт из чужих файлов можно сделать "как душе угодно", раз вашего "родного формата" в чужих файлах нет :)


Название: Re: Import As Open
Отправлено: Igors от Март 12, 2013, 09:32
Что если реализовать функции преобразования модели из формата хранения в оперативный и обратно без потерь данных?
А что здесь значит "без потерь"? Добавить все данные нативного формата в оперативный? Это никак не годится т.к. число нативных данных огромно. По существу сейчас я сделал так:

- из объекта в памяти выделить те данные что нужны оперативному формату
- создать на их основании оперативные/промежуточные данные и выполнить действия над ними
- перевести назад в нативный и сериализовать

Заметим что это "для объекта в памяти" и действия производятся во время сериализации. А иначе, читая нативный файл я должен что-то делать с большинством данных которые в оперативный формат не входят - но куда я их дену?

Др проблема в том что диагностика может потребовать создания/удаления объектов, но это недопустимо на фазе сериализации


Название: Re: Import As Open
Отправлено: schmidt от Март 12, 2013, 21:31
Цитировать
Это никак не годится т.к. число нативных данных огромно.

Как вариант - представить оперативный формат как расширение нативного, чтобы избежать ненужного копирования. То есть сделать так, чтобы к нативному формату "наращивались" оперативные данные. А после ряда модификаций оперативных данных осуществлять вызов некого syncData(), применяющего изменения оперативных данных к "нативной" части. Получилось бы что "расширенный" формат объединял бы в себе нативные и оперативные данные.



Название: Re: Import As Open
Отправлено: Igors от Март 13, 2013, 16:23
Как вариант - представить оперативный формат как расширение нативного, чтобы избежать ненужного копирования. То есть сделать так, чтобы к нативному формату "наращивались" оперативные данные.
Ну а смысл этого агрегата? Если объект (нативные данные) загружается, то он фиксируется в списках объектов, резолвятся его парент и чилдрены и.т.п. Создавать еще какую-то др загрузку (которая живет без всех обвязок) минимум накладно (если вообще реально). С др стороны в нативных данных есть все, так чего спешить с созданием оперативных? Вот и получается

- загрузить нативные данные (можно втихаря, юзверю не показывать)
- сериализовать их, на ходу создавая оперативные
- удалить нативные данные (это конечно имеется)

Тут правда, undo путается под ногами