Завершающий день марафона по разработке графически-рисовательного приложения с Qt! Понадобилось ровно столько дней, сколько и было отмерено изначально ) При этом форсировать на промежуточных этапах не пришлось 👌 Вкратце расскажу, что я добавил для финальной на данный момент версии...
Во-первых, конечно, пресловутый дизайн, которым мы сознательно не озадачивались в процессе разработки логики и механики создания объектов. В целом, кардинальных изменений это не внесло, расположение элементов интерфейса вообще шло четко по первоначальной схеме, я набросал иконки для кнопок, подправил цвета на свой вкус. Итог получился такой:
Для добавления иконок выкидываем названия кнопок из соответствующих моделей и вместо них добавляем имена файлов, на примере ToolPanel
:
Rectangle { id: rootRectangle color: "transparent" height: buttonsGrid.height property int selectedTool: PaintEntity.ToolType.Cuboid property var buttonsModel: [ {iconFileName: "select.png", type: PaintEntity.ToolType.Select}, {iconFileName: "delete.png", type: PaintEntity.ToolType.Delete}, {iconFileName: "cuboid.png", type: PaintEntity.ToolType.Cuboid}, {iconFileName: "sphere.png", type: PaintEntity.ToolType.Sphere}, {iconFileName: "cone.png", type: PaintEntity.ToolType.Cone}, {iconFileName: "cylinder.png", type: PaintEntity.ToolType.Cylinder}, {iconFileName: "torus.png", type: PaintEntity.ToolType.Torus}, {iconFileName: "extruded_text.png", type: PaintEntity.ToolType.ExtrudedText}, {iconFileName: "curve.png", type: PaintEntity.ToolType.Curve}, {iconFileName: "delete_all.png", type: PaintEntity.ToolType.DeleteAll} ] // ...............
Соответственно, далее эти данные помещаем туда, где им и место:
Button { checkable: true width: (rootRectangle.width - (buttonsGrid.columns - 1) * buttonsGrid.spacing) / buttonsGrid.columns height: width icon.width: 0.8 * width icon.height: 0.8 * height icon.color: "transparent" icon.source: "../icons/tools/" + modelData['iconFileName'] property var type: modelData['type'] // ...............
В принципе, все изменения относительно вчерашней версии и так видны на скрине ) Отчасти для получения полезной информации, отчасти ради большего оживления интерфейса я добавил окно с текущими параметрами и всплывающее сообщение при выборе или удалении объекта:
В подробности реализации не погружаемся, исходники будут там же, где и обычно 👍 Ах да, я еще добавил инструмент для удаления всех объектов. Вообще он мне не особо нужен, но расположив кнопки по пять в ряд, оказалось, что 9 кнопок на 5 нацело не делятся, поэтому и была добавлена 10-я, юбилейная:
Ну и при ее использовании происходит полная очистка сцены:
Кроме того, очевидно, что никак не обойтись без возможности сохранения и загрузки текущих объектов. Для этого добавились два диалоговых окна соответственно для сохранения и загрузки:
FileDialog { id: saveFileDialog title: qsTr("Save") defaultSuffix: qsTr("mtPaint3D") selectExisting: false onAccepted: { paintEntity.save(saveFileDialog.fileUrl); } } FileDialog { id: loadFileDialog title: qsTr("Load") nameFilters: [qsTr("*.mtPaint3D")] onAccepted: { paintEntity.load(loadFileDialog.fileUrl); } }
В результате пользовательких действий происходит вызов save()
или load()
из PaintEntity
. В целом же, механизм следующий... Для каждого из существующих на сцене PaintObject
'ов вызывается метод save()
, далее сохраняются последовательно базовые и уникальные параметры объектов - saveBasicData()
и saveUniqueData()
. В случае необходимости методы переопределяются в объектах, которые наследуют от PaintObject
. Так для кривой Безье процесс сохранения и загрузки получился, в принципе, своим собственным. Для текста переопределяется только saveUniqueData()
в силу наличия собственно текста, который также нужно подвергнуть сохранению.
Вот как-то так. Меня и результат, и процесс, и затраченное время полностью удовлетворили, надеюсь прочтение и отслеживание прогресса также несли в себе некие приятно-познавательные нотки 👍 А на этом завершаем, посмотрим, как зайдет данный формат, всем большое спасибо за внимание! )
Итоговый исходный код и проект: mtPaint3D