| | | | |
Контейнер визуальных объектов | Полный текст материала
Другие публикации автора: Юрий Спектор
Цитата или краткий комментарий: «... Рассмотрим достаточно распространенную практическую задачу: необходимо реализовать визуальный контейнер, на котором размещаются графические объекты. Эти объекты должны быть не только нарисованы на поверхности этого контейнера, но и доступны пользователю для различных манипуляций, таких как выделение, перемещение с помощью мыши и др. Объекты могут быть разнотипными, следовательно, они будут по-разному выглядеть, характеризоваться различными параметрами. Набор операций, которые можно совершать по отношению к ним, также будет различным. ...» |
Важно:- Страница предназначена для обсуждения материала, его содержания, полезности, соответствия действительности и так далее. Смысл не в разборке, а в приближении к истине :о) и пользе для всех.
- Любые другие сообщения или вопросы, а так же личные эмоции в адрес авторов и полемика, не относящаяся к теме обсуждаемого материала, будут удаляться без предупреждения авторов, дабы не мешать жителям нормально общаться.
- При голосовании учитывайте уровень, на который расчитан материал. "Интересность и полезность" имеет смысл оценивать относительно того, кому именно предназначался материал.
- Размер одного сообщений не должен превышать 5К. Если Вам нужно сказать больше, сделайте это за два раза. Или, что в данной ситуации правильнее, напишите свою статью.
Всегда легче осудить сделанное, нежели сделать самому. Поэтому, пожалуйста, соблюдайте правила Королевства и уважайте друг друга.
Добавить свое мнение.
| | Содержит полезные и(или) интересные сведения | [1] | 19 | 95% | | | | Ничего особенно нового и интересного | [2] | 1 | 5% | | | | Написано неверно (обязательно укажите почему) | [3] | 0 | 0% | | Всего проголосовали: 20 | | | Все понятно, материал читается легко | [1] | 19 | 90.5% | | | | Есть неясности в изложении | [2] | 2 | 9.5% | | | | Непонятно написано, трудно читается | [3] | 0 | 0% | | Всего проголосовали: 21 |
[GDI, рисование на канве]
Отслеживать это обсуждение
Всего сообщений: 8211-04-2012 15:06сообщение от автора материала Кстати, у "контейнера визуальных объектов" - EasyCAD - есть свой сайт: http://easycad-lib.com Не сочтите рекламой, к автору не имею никакого отношения. А у уважаемого автора хочу поинтересоваться: как спроектировали это чудо? Может, посоветуете литературу какую? Хочу также научиться :)
Здравствуйте! Спасибо за отзыв! Проект, который начинался всего лишь с обучающей статьи, действительно разросся, последние годы моя работа так или иначе связана с "Контейнером визуальных объектов". :) Если есть к теме повышенный интерес и желание пообщаться, можете обращаться в личку, по электронной почте, или скайпу, на сайте есть мои контакты :) Что касается проектирования, литературы на этот счет полно, и хорошей, но мое мнение, что читать ее нужно уже тогда, когда и сам до всего созрел, только лишь с целью навести порядок у себя в голове, разложить все по полочкам. Так что Geo прав:
Постоянно отвечая на Круглом Столе на вопросы о том, как создать редактор схем :D
Типа того :) Не сочтите за рекламу Круглого стола. Хотя, почему же... Сочтите! :) |
|
11-04-2012 12:43>>> как спроектировали это чудо?
Постоянно отвечая на Круглом Столе на вопросы о том, как создать редактор схем :D
P.S. Я тоже не автор |
|
11-04-2012 12:37 Кстати, у "контейнера визуальных объектов" - EasyCAD - есть свой сайт: http://easycad-lib.com Не сочтите рекламой, к автору не имею никакого отношения. А у уважаемого автора хочу поинтересоваться: как спроектировали это чудо? Может, посоветуете литературу какую? Хочу также научиться :) |
|
11-04-2012 06:09сообщение от автора материала Что-то у меня исходный контейнер визуальных объектов не работает (((
Delphi 7, выдает сообщение "Error Reading Form. Class TVisualContainer not found...". и потом при попытке компиляции "Field Form1.VisualContainer1 does not have a corresponding component". Не могу понять, в чем дело - исходник на месте!
Здравствуйте! Установите пакет VisCont.dpk из исходников к статье |
|
07-04-2012 17:39Что-то у меня исходный контейнер визуальных объектов не работает (((
Delphi 7, выдает сообщение "Error Reading Form. Class TVisualContainer not found...". и потом при попытке компиляции "Field Form1.VisualContainer1 does not have a corresponding component". Не могу понять, в чем дело - исходник на месте! |
|
22-12-2010 06:10сообщение от автора материала Либо где-то у вас ошибка, либо возможно дело в том, что я там в коде поставил проверку, что 0-я вершина должна быть всегда выше и левее 1-й. Проверьте, не пытаетесь ли вы сделать инач, либо просто уберите эту проверку |
|
22-12-2010 06:06В целом разобрался, блок рисуется но vertex[0]и vertex[1] почему то не реагирует на координату "Y" Тоесть врехняя грань блока всегда на верхней границе VisualContainer хотя 2 и 3 вершины двигаюся. Масштаб не помогает. Пока не пойму в чем дело. |
|
22-12-2010 04:42Спасибо большое! Буду разбираться. |
|
22-12-2010 03:33сообщение от автора материала Block.Vertex[2] := Pt;
Наверное там с нуля нумерация вершин, так что на Block.Vertex[1] и Block.Vertex[0] исправьте соответственно |
|
22-12-2010 03:32сообщение от автора материала В программе
Не совсем, примерно так (пишу код прямо сюда, чуть что подправьте):
var
Block: TBeginEndBlock;
Pt: TFloatPoint;
begin
Block := TBeginEndBlock.Create;
Pt.X := 200;
Pt.Y := 200;
Block.Vertex[2] := Pt;
Pt.X := 100;
Pt.Y := 100;
Block.Vertex[1] := Pt;
VisualContainer.AddObject(Block); |
|
22-12-2010 01:34Что то типо такого?
добавить в контейнере
procedure TVisualContainer.addobject (incomingObject: TBaseVisualObject);
begin
Selected := FObjects.Add(incomingObject);
Invalidate;
end;
В программе
procedure TForm1.btn1Click(Sender: TObject);
begin
VisualContainer1.ObjectType := TVisualObjectClass((Sender as TComponent).Tag);
VisualContainer1.Objects.Create;
??????настраиваем параметры и положение еще бы понять как.
VisualContainer1.addobject;
end;
Что то голова вообще перестала соображать все забыл ((( |
|
21-12-2010 09:30сообщение от автора материала 1. Чуть-чуть доработайте контейнер, чтобы объекты в него можно было вставлять в ран-тайм, достаточно простого метода AddObject, который принимает объект параметром, заносит его в список FObjects и вызывает перерисовку (Invalidate).
2. Создаем объект вызовом конструктора Create, настраиваем параметры, положение, и добавляем в контейнер методом AddObject |
|
20-12-2010 06:10Давно искал что то подобное однако уровня моего программирования явно не хватает. Может приведет кто нибудь пример как мне нарисовать пару блоков с линией используя этот контейнер но не вручную а из программы. |
|
29-11-2010 10:55сообщение от автора материала Поясню. Типов объектов много, каждый тип может выполнять свои специфические действия. Хотелось иметь возможность через интерфейс базового предка всех объектов уметь эти действия выполнить без необходимости проверять и приводить типы. Не знать конкретный тип объекта, с которым работаешь, иногда полезно, это уменьшает количество жестких связей в системе и код нашего контейнера не нужно жестко на этих типах завязывать.
Использование сообщений через Dispatch позволяет реализовать этот подход - мы любому объекту можем отправить любую команду, если он ее обрабатывает - он ее обработает, иначе - вызовет DefaultHandler, в котором можно либо ничего не делать, либо как-нибудь обработать ситуацию. Я не настаиваю что такой подход лучше чем просто объявить пару методов, нет, он лучше в том случае, если нужна универсальность, но за нее нужно платить тем, о чем Вы говорите |
|
26-11-2010 07:34Для чего нужно было использовать отправку "сообщений"?
Придумывать как в 2 параметра SendMessage "запихнуть" нужную информацию для отправки.
Делать кучу структур, типа TVOCBeginDrag.
Ведь можно было бы сделать функции, типа:
Type
TDisplayObj = class
Public
Procedure OnStart(X, Y: LongInt);
Procedure OnMove(X, Y: LongInt);
Procedure OnEnd(Align: LongInt = 0);
End;
|
|
18-04-2010 02:39Спасибо за статью :)
Я в ближайшем будущем планирую заняться чем-то подобным (ради спортивного интереса), поэтому изучаю, что существует в природе. Бесплатные компоненты, которые мне уже довелось посмотреть, используют, на мой взгляд, принципиально не правильный подход (объекты наследуются от TGraphicControl, в качестве контейнера - TScrollBox или что-то вроде того) - очень непрозрачное управление объектами, жуткие тормоза прорисовки при большом кол-ве объектов.
А тут как раз всё абстрагировано, то что надо :)
Исходники я ещё не смотрел, но уже созрело такое замечание/предложение. У Вас у объектов хранятся координаты базовых вершин, при перемещении объекта надо изменять их все. А что, если положение объекта (координаты X,Y первой базовой вершины + возможно коэффициенты трансформации, угол поворота объекта и т.п.) хранить в контейнере (или в родительском объекте), а все вершины самого объекта будут хранить координаты относительно первой вершины объекта (причём саму вершину хранить не надо, у неё координаты (0,0))?
Понимаю, что в таком случае усложняется контейнер - надо хранить не только объект, но и его положение (+ возможно, его трансформации относительно контейнера - сразу на ум приходит необходимость структуры, которая будет хранить ссылку на объект, координаты 1й вершины и прочие коэффициенты трансформации и наклона). Усложнится процедура конвертирования координат, может быть что-то ещё.
Но с точки зрения объекта - как бы это выразиться... объект будет сам в себе, а для внешнего мира достаточно передать свою ширину и высоту (чтобы нарисовать прямоугольник выделения). Станет гораздо проще создавать сложные объекты (состоящие из большого числа графических примитивов).
Вобщем, мне кажется, такой подход может быть оправдан, надо будет ещё подумать над всем этим (а главное - над постановкой задачи :))... |
|
29-01-2010 07:23сообщение от автора материала Понятно. Возможно в ней юнит Themes отличается от того, что в более поздних. К сожалению, нет этой версии чтобы проверить, но судя по всему оно так. Функции из этого юнита используются в редакторах свойств для рисования элементов управления в стиле XP. Замените просто их вызовы на что-нибудь более классическое, DrawFrameControl, например |
|
29-01-2010 05:35 Какая у вас версия Delphi?
Delphi 7 |
|
28-01-2010 04:18сообщение от автора материала Undeclared identifier: 'ThemeControl'
Какая у вас версия Delphi? |
|
26-01-2010 04:07Здравсвуйте! При компиляции пакета выдает следующие ошибки:
[Error] ECADPropertyEditors.pas(627): Undeclared identifier: 'ThemeControl'
[Fatal Error] ECADObjectInspector.pas(9): Could not compile used unit '..\Sources\CommonControls\ECADPropertyEditors.pas'
|
|
15-12-2009 05:05сообщение от автора материала Александр Щукин, спасибо за интерес к данной теме! Насчет Вашего кода - там действительно несмотря на функциональную схожесть, решения совсем другие, общего, с первого взгляда, не так уж и много. Вы можете связаться со мной по ICQ, тогда бы мы могли с вами обсудить примененные в наших проектах подходы и приемы, обменяться мнениями на этот счет :) |
|
14-12-2009 14:43Новая ссылка на исходники SimpleCAD: http://depositfiles.com/files/hiaw2f866 (проверено - вирусов нет)
Понимаю, что программа в зачаточном состоянии и сырая. Просто хочется ее показать. Буду рад любым отзывам, советам и критике |
|
14-12-2009 13:50>>> Не стыдно, гражданиа Щукин, глистами народ пугать?
Простите великодушно! Моего злого умысла здесь и не было. Просто мой антивирус почему-то его не видит.
А в общем, этот глист, насколько я знаю безвреден, сильный дискомфорт ощущаться не должен.
Еще раз перед всеми извиняюсь. Сылку на файл удалил. Выложу заново, как только избавлюсь от этого вируса
|
|
14-12-2009 13:19"Гражданин Щукин" в данном случае не злоумышленник, а жертва. Это тот самый Win32.Induc, заражающий саму Delphi, который был выявлен на Королевстве.
Подробности в новостях
http://delphikingdom.com/news/index.asp?Day=12&Month=8&Year=2009
В блоге Александра Алексеева (главного охотника)
http://gunsmoker.blogspot.com/2009/08/delphi-delphi.html
to Александр Щукин:
Настоятельно рекомендую прочитать приведенные ссылки, а потом принять меры у себя. В частности, пересобрать все проекты. Иначе Вы несете угрозу для собратьев по оружию -- программистов Delphi, так как единственное, что делает данный зловред, -- заражает Delphi. |
|
14-12-2009 11:18 Да, Аваст тоже нашел червячка. Не стыдно, гражданиа Щукин, глистами народ пугать? |
|
14-12-2009 09:59На всякий случай будьте внимательней. Майкросовтовский антивирус ругнулся. |
|
14-12-2009 06:22Здравствуйте Ins!
Приблизительно год назад, когда Вы опубликовали эту статью я тоже заразился идеей о создании векторного редактора. С тех пор в свое свободное время также занимаюсь разработкой похожей программы (по воли случая даже название похоже - SimpleCAD).
За основу я конечно же взял Вашу идею с логической канвой. Дальше разработка программы пошле несколько инным путем. Основной упор я делал на создание редактора для машиностроительного черчения (хотя на данном этапе разработки, наверное, трудно об этом догадаться :) ).
Буду Вам очень признателен, если выскажите свое мнение о программе.
Ссылка для скачивания исходников: http://depositfiles.com/files/p15eoguqy
В этом же архиве есть общая диаграмма классов, построенная в Rational Rose 7.0.0.
PS
по образованию я не программист, по этому не судите строго :)
|
|
07-11-2009 03:51сообщение от автора материала Как смотрит автор на то, что исходники, приложенные к статье, будут взяты за основу коммерческого приложения?
На здоровье! :) |
|
07-11-2009 02:09 Как смотрит автор на то, что исходники, приложенные к статье, будут взяты за основу коммерческого приложения? |
|
06-11-2009 14:13сообщение от автора материала Зависает при открытии VisCont.dpk, даже компилится запустить не могу ((
Может вышлите на мыло обновлённый вариант (ladyshack@yandex.ru).
Интересный эффект, даже не знаю чем это может быть вызвано. Попробуйте не использовать это пакет, а создайте свой и включите в него файлы из статьи. Что касается обновленного варианта, то с момента публикации данные файлы никак не редактировались, т.е. у вас последняя версия примера. |
|
06-11-2009 12:58Добрый день.
Статья очень нужная и полезная, вот только проблема с исходниками. Загрузила, а открыть в Д2005 не получается. Зависает при открытии VisCont.dpk, даже компилится запустить не могу ((
Может вышлите на мыло обновлённый вариант (ladyshack@yandex.ru).
Спасибо. |
|
31-10-2009 12:59сообщение от автора материала мордехай вулкан, я обновил файлы, попробуйте новые |
|
31-10-2009 10:12сообщение от автора материала мордехай вулкан, тут я вам точно ничего сказать не могу, у меня подобные ошибки не воспроизводятся, что-то возможно не так в вашей версии. Единственное что могу предложить - свяжитесь со мной по мылу, могу выслать свое.
Новая версия значительно более "задумчивая", чем первая, приложенная к статье.
Но это еще не готовый к использованию вариант, а предварительный, для тестирования |
|
31-10-2009 06:45Тогда уж сразу, в догонку. Новая версия значительно более "задумчивая", чем первая, приложенная к статье. |
|
31-10-2009 06:42Ок, скомпилировал. Теперь сразу при запуске cкомпилированной программы выдает: Could not convert variant of type(OleStr) into type(Double)
Далее, попытка создания нового листа влечет: Access violation at adress 004DED7E in module 'DiagramDesigner.exe'. Read of address 00000278.
Создаю объект. Навожу на него мышь: Range check error. На этом пока тестирование останавливаю.
|
|
30-10-2009 11:22сообщение от автора материала мордехай вулкан, странно, там действительно была такая ошибка, но вроде бы я заливал ее исправление. Что сделать, чтобы ее исправить: занесите реализацию метода внутрь этой директивы, вот так:
procedure TECADPluginObject.RotatePoint(Point1, Point2: TECADFloatPoint);
begin
Host.RotatePoint(Point1, Point2);
end;
Вообще, может в Вашей версии есть еще подобные ошибки, просто проследите, чтобы метод, который объявлен в интерфейсной части юнита под этой директивой, был внутри нее и в секции implementation, и наоборот, если в интерфейсной части метод не под этой директивой, то и в implementation он должен быть не внутри IFDEF. Я какое-то время не замечал этой ошибки, так как тестирую всегда с включенной директивой в опциях проекта, и компилятор метод находил. Вообще, смысл этой директивы в том, что когда она включена, некоторые объекты на схеме поддерживают операции поворота на произвольный угол. |
|
30-10-2009 05:20 Скачал по указанному адресу https://sourceforge.net/projects/easycad/ исходники, но скомпилировать не получается ни в Д7, ни в Турбо2006. Ругается так: [Pascal Error] ECADPluginsCommon.pas(428): E2003 Undeclared identifier: 'RotatePoint'
[Pascal Error] ECADPluginsCommon.pas(428): E2029 ';' expected but '(' found
[Pascal Error] ECADPluginsCommon.pas(430): E2003 Undeclared identifier: 'Host'
[Pascal Error] ECADPluginsCommon.pas(430): E2003 Undeclared identifier: 'Point1'
[Pascal Fatal Error] ECADPluginsHost.pas(9): F2063 Could not compile used unit 'ECADPluginsCommon.pas'
Есть мнение, что дело в директиве компилятора IFDEF SPACEANGLE. Найти по этой директиве в справке ничего не смог. Возможно, я ошибаюсь. Как быть? |
|
14-10-2009 05:54сообщение от автора материала Подскажите, как удобней используя Ваш пример реализовать объект "связь между объектами"
Get-методы базовых точек пусть возвращают координаты не из собственного массива или полей, а запрашивают их у объектов, к которым привязаны |
|
14-10-2009 05:21Подскажите, как удобней используя Ваш пример реализовать объект "связь между объектами"
По идее это должна быть ломанная линия 2 крайние базовые точки которой являются одновременно и базовыми точками прямоугольника или другой фигуры. Ну и соответственно при перетаскивании этих объектов эта "связь" должна растягиваться изменять положение... |
|
20-09-2009 14:17Доброго времени суток, Юрий!
Изучил статью и выложенные исходники. Поигрался :)
Написал в аську выложенную в профиль. :)
На всякий случай решил продублировать здесь.
Не хотите поработать в этом направлении на небезвозмездной основе?
С уважением, Косов Дмитрий |
|
18-09-2009 00:11
17-09-2009 08:49
16-09-2009 02:55 Отлично! Готовится ли вторая часть? Если не готовится, то можно попросить автора поделиться - пусть и "сырыми" - наработками? Очень нужно. |
|
21-05-2009 03:53сообщение от автора материала Denis Serebraykov, напишите свой адрес, я Вам юнит вышлю, посмотрите как там реализованы интересующие Вас вещи. |
|
21-05-2009 02:57сообщение от автора материала Но вот как поступить с изменением размеров?
Хм, а что с ним? Изменение размеров родителя интересует или дочерних? Вроде как изменение размеров ни того, ни другого, на координатах никак не скажется. Ну изменился размер, а нам какое дело? Все равно отсчитываем от вершины родителя. Точно так же ведь и компоненты на форме себя ведут. У формы Left/Top относительно экрана, у компонентов - относительно формы или другого родителя. И как тут изменение размеров мешает?
При вызове метода Free у меня возникает ошибка работы с памятью, так как объект контейнер хранит ссылки на удаленные объекты. Разъясните пожалуйста, как реализовать перенос самого объекта из одного списка в другой, а не ссылку на него (из списка контейнера в список объекта-родителя)? Или где это можно прочитать, а то для меня эта тема темный лес :)
Так, не совсем понял, если честно, почему возникает ошибка. Я бы сделал так: свойству Parent сделал бы сеттер, который извлекает объект из списка старого родителя (Extract метод), и заносит в список нового. А в деструкторе базового класса всех объектов - удаление все деток. Соотвтественно разгруппировка - это перенос всех объектов из группы, а потом удаление последней, а удаление группы со всеми объектами - просто Free. |
|
20-05-2009 05:47
Почему не хотите пойти по такому пути?
От недопонимания. С перемещением мне понятно (Относительная координата = левая верхняя координата родителя - текущая координата потомка). Но вот как поступить с изменением размеров? Если не трудно, то могли бы вы привести пример кода, для лучшего понимания. И я так понял, что поле FChildren перенести из объекта группы в базовый объект?
Ну, в простейшем случае можно сделать так, чтобы при уничтожении объект автоматом прибивал и всех своих деток.
При вызове метода Free у меня возникает ошибка работы с памятью, так как объект контейнер хранит ссылки на удаленные объекты. Разъясните пожалуйста, как реализовать перенос самого объекта из одного списка в другой, а не ссылку на него (из списка контейнера в список объекта-родителя)? Или где это можно прочитать, а то для меня эта тема темный лес :) |
|
20-05-2009 02:47сообщение от автора материала Что-то мне подсказывает, что я сделал все неверно. Можете указать на ошибки?
Ну, я делал немного по-другому. Как уже сказал, координаты дочерних объектов пересчитывал в относительные, и тогда при перемещении ничего никому рассылать не требуется. Перемещение просто меняет координаты самой группы, а относительные координаты остаются неизменными. В общем, просто нужно ввести такую систему, чтобы координаты задавались относительно родителя, а не документа, если родитель есть, и при рисовании или мышиных операциях это учитывать. Почему не хотите пойти по такому пути? Несложно в реализации да и дополнительные бонусы дает, кроме возможности группировки, например - можно "привязывать" текстовые надписи к фигурам, которые будут таскаться вместе с ними, сохраняя свое относительное расположение.
И еще я не понял, как мне сделать удаление группы, т.е. удалить не только объект группа, но и её потомков.
Ну, в простейшем случае можно сделать так, чтобы при уничтожении объект автоматом прибивал и всех своих деток. Тогда удаление группы со всеми элементоми - это просто вызов Free. А разгруппировку реализовать по такому алгоритму:
1. В цикле по всем элементам переносим их из группы на непосредственно документ, не забывая менять координаты, чтобы абсолютные остались неизменными
2. Удаляем пустой объект-группу |
|
19-05-2009 11:51
Как-нибудь так: контейнер хранит ссылку на документ, документ содержит список объектов без родителей, объекты хранят списки своих дочерних объектов, которые в свою очередь тоже могут содержать дочерние и т.д.
Я поступил следующим образом:
1. Добавил в контейнере две операции: группировка и разгруппировка
TVisualContainer = class(TCustomControl, ICoordConvert)
...
public
procedure Group;
procedure UnGroup;
...
end;
procedure TVisualContainer.Group;
var
i : integer;
begin
FCurrentObject := TGroup.Create;
FCurrentObject.OnChange := ObjectChanged;
Selected := FObjects.Add(FCurrentObject);
for i := 1 to FObjects.Count-1 do begin
if TBaseVisualObject(FObjects[i-1]).Parent = nil then (FCurrentObject as TGroup).AddObject(TBaseVisualObject(FObjects[i-1]));
end;
end;
procedure TVisualContainer.UnGroup;
var
i : integer;
begin
if FCurrentObject is TGroup then begin
Selected := -1;
i := FObjects.IndexOf(FCurrentObject);
FObjects.Delete(i);
Invalidate;
end;
end;
2. Добавил новый объект группа:
TGroup = class(TRectVisualObject)
private
FChildren : TObjectList;
procedure UpdatePoints;
procedure VOCMove(var Command: TVOCMove); message VOC_MOVE;
public
constructor Create; override;
destructor Destroy; override;
procedure AddObject(AObject:TBaseVisualObject);
end;
constructor TGroup.Create;
begin
inherited Create;
FChildren := TObjectList.Create(True);
end;
destructor TGroup.Destroy;
var
j : integer;
begin
for j := 1 to FChildren.Count do TBaseVisualObject(FChildren[j-1]).Parent := nil;
inherited Destroy;
end;
procedure TGroup.AddObject(AObject:TBaseVisualObject);
begin
FChildren.Add(AObject);
AObject.FParent := Self;
UpdatePoints;
end;
procedure TGroup.UpdatePoints;
var i,j: integer;
P1,P2:TFloatPoint;
begin
if FChildren.Count = 0 then exit;
P1 := TBaseVisualObject(FChildren[0]).BasePoints[0];
P2 := TBaseVisualObject(FChildren[0]).BasePoints[0];
BeginUpdate;
for j := 1 to FChildren.Count do begin
for i:=1 to TBaseVisualObject(FChildren[j-1]).BasePointsCount do
begin
P1.x := min(P1.x,TBaseVisualObject(FChildren[j-1]).BasePoints[i-1].x);
P1.y := min(P1.y,TBaseVisualObject(FChildren[j-1]).BasePoints[i-1].y);
P2.x := max(P2.x,TBaseVisualObject(FChildren[j-1]).BasePoints[i-1].x);
P2.y := max(P2.y,TBaseVisualObject(FChildren[j-1]).BasePoints[i-1].y);
end;
end;
BasePoints[0] := P1;
BasePoints[1] := P2;
EndUpdate;
end;
procedure TGroup.VOCMove(var Command: TVOCMove);
var
i: Integer;
begin
inherited;
for i := 1 to FChildren.Count do TBaseVisualObject(FChildren[i-1]).SendCommand(Command.CmdID, longint(Command.DeltaX), longint(Command.DeltaY));
end;
Алгоритм работы создания группы:
1. Создаю объект группа
2. Добавляю с помощью процедуры AddObject ссылку на объект и присваиваю объекту родителя
3. Обновляю контрольные точки группы
4. Когда группе приходит сообщение она рассылает его всем потомкам (В примере сообщение на перемещение).
Алгоритм работы удаления группы:
1. У потомков удаляю родителя
2. Удаляю сам объект группа.
Что-то мне подсказывает, что я сделал все неверно. Можете указать на ошибки?
И еще я не понял, как мне сделать удаление группы, т.е. удалить не только объект группа, но и её потомков.
|
|
21-04-2009 13:01сообщение от автора материала А кто будет тогда хранить информацию о дочерних объектах? Контейнер или объект-родитель?
Как-нибудь так: контейнер хранит ссылку на документ, документ содержит список объектов без родителей, объекты хранят списки своих дочерних объектов, которые в свою очередь тоже могут содержать дочерние и т.д.
Может лучше не переходить к относительным координатам, а просто в объекте-родителе рассылать сообщения дочерним объектам?
Не думаю, что это лучше. А в чем проблема то? Переход из относительных координат в абсолютные и наоборот очень прост. |
|
21-04-2009 12:01
1. Создать группирующий объект
2. Поместить в него дочерние объекты, изменив их координаты базовых точек таким образом, чтобы относительно самого документа они остались неизменными (теперь координаты будут задаваться относительно группирующего объекта).
А разгруппировка - обратное действие.
А кто будет тогда хранить информацию о дочерних объектах? Контейнер или объект-родитель?
Может лучше не переходить к относительным координатам, а просто в объекте-родителе рассылать сообщения дочерним объектам? |
|
21-04-2009 07:27сообщение от автора материала Тут еще вот что. Объекты имеют Z-Order, который определяет порядок и рисования. Может получится так, что объект скрыт под другими и выделен. В этом случае, если рамочку выделения будет рисовать не контейнер, то ее не будет видно вовсе, а это не есть гуд. Хорошо бы показать границы выделенного объекта даже если он сам скрыт. Пользователь должен знать что за объект выделен. Например для того, чтобы знать что произойдет, если он нажмет на кнопку Delete ;) |
|
21-04-2009 07:20>>> Мне кажется, что более правильно было бы сообщить объекту(ам), а они бы уже рисовали, причем, быть может, каждый по-своему, нет?
Насколько я понимаю, в общем случае должно быть и то, и это. Поясняю...
Возьмем тот же Visio. Если мы Выделим группу объектов, то, во-первых, у нас каждый объект, входящий в выделение, должен выглядеть иначе, чем не выделенный; во-вторых, должна быть отрисована общая рамка вокруг всего выделения. Разумно, чтобы внешний вид выделенного объекта рисовался самим объектом, а окружающую рамку всего выделения должен рисовать контейнер.
В частном случае, думаю, чего-то одного может и не быть (например, если мы не можем выделять группу объектов, соответственно, выделять можно только один объект, то он сам может рисовать и рамку выделения с квадратиками контрольных точек). |
|
21-04-2009 02:51сообщение от автора материала А теперь вопрос: каким образом в данном примере можно реализовать группировку объектов? По типу как в Corel Draw или Word?
Скорее всего, данный пример для этого потребует доработки. Я ниже писал, что
2. ВО теперь представляют собой не плоский список, а организованы в виде дерева. Любой ВО в общем случае может являться родителем для других ВО. Базовые точки задаются в относительных величинах, а началом координат для них является Origin родителя. Таким образом, при перемещении родитель перемещается со всеми его дочерними ВО.
Можно поступить таким же образом. А потом ввести класс группирующего объекта, цель которого просто быть родителем для других и перехватывать мышиные операции, не передавая их своим чаилдам, чтобы пользователь таскал мышью саму группу. Таким образом операция группировки - это:
1. Создать группирующий объект
2. Поместить в него дочерние объекты, изменив их координаты базовых точек таким образом, чтобы относительно самого документа они остались неизменными (теперь координаты будут задаваться относительно группирующего объекта).
А разгруппировка - обратное действие.
to чиполинский
Скажите, пожалуйста, сколько времени заняло создание того, что описано в статье, и сколько заняло усовершенстование (нужно знать для правильной расстановки ориентиров на будудщее)?
Не знаю, как ответить на этот вопрос. Если скажу, что от начала публикации статьи до теперешнего момента - то сильно Вас шокирую? А если уточню, что занимаюсь этим с свободное время и при наличии желания (которого чаще нет :) ), и в сумме на все про все ушло около сотни рабочих часов? Но тоже преувеличу, а точнее преуменьшу, так как наработки уже были, я по сути просто их собираю вместе.
И это... как бы скачать исходники с изменениями(ведь, насколько понимаю, проект открытый)?
Там все сыро, недоделано, и с ошибками. Устроит? :)
И последнее. Лично меня нескольок смутило то, что контейнеру поручено "отрисовывать состояние выделенности". Мне кажется, что более правильно было бы сообщить объекту(ам), а они бы уже рисовали, причем, быть может, каждый по-своему, нет?..
Если у Вас вид выделенного объекта может зависеть от его типа, то разумеется. Мне же тут повезло больше, достаточно нарисовать вершинки, которые есть у всех объектов. Да, и еще. Если будете делать так, как Вы предложили, учтите режим, при котором выделение при рисовании игнорируется. При печати же Вам скорее всего выделение показывать не нужно будет?! В моем случае его просто не нужно рисовать поверх объектов, а в Вашем нужно как-то учесть. |
|
20-04-2009 10:55К сообщению Юрия Спектора от 16-02-2009 03:16.
Глянуть на озвученные изменения хоть краешком глаза - и можно смело помирать! :-) Немножо не в тему, но очень интересует такой вопрос. Скажите, пожалуйста, сколько времени заняло создание того, что описано в статье, и сколько заняло усовершенстование (нужно знать для правильной расстановки ориентиров на будудщее)? И это... как бы скачать исходники с изменениями(ведь, насколько понимаю, проект открытый)? И последнее. Лично меня нескольок смутило то, что контейнеру поручено "отрисовывать состояние выделенности". Мне кажется, что более правильно было бы сообщить объекту(ам), а они бы уже рисовали, причем, быть может, каждый по-своему, нет?.. |
|
20-04-2009 04:40Статья очень понравилась, узнал много нового для себя, автор молодец!
А теперь вопрос: каким образом в данном примере можно реализовать группировку объектов? По типу как в Corel Draw или Word? |
|
16-02-2009 03:16сообщение от автора материала Идея, кстати, получила дальнейшее развитие. Ниже приведу список некоторых нововведений и пересмотров, может кто переймет идею и реализует сам, а может как-нибудь созрею, чтобы написать к статье дополнение:
1. Контейнер теперь не является вместилищем визуальных объектов (далее ВО). Они содержаться в объекте "документ", который в свою очередь может быть загружен в контейнер. Документ реализует субъект паттерна "наблюдатель", а контейнер(ы) - слушатели. Любое изменение в документе приводит к уведомлению всех его слушателей, в частности - к перерисовке контейнера.
2. ВО теперь представляют собой не плоский список, а организованы в виде дерева. Любой ВО в общем случае может являться родителем для других ВО. Базовые точки задаются в относительных величинах, а началом координат для них является Origin родителя. Таким образом, при перемещении родитель перемещается со всеми его дочерними ВО. Также кроме смещения родитель может задать угол поворота. Таким образом, каждый ВО задает для дочерних свою систему координат, которая является суперпозицией смещения и поворота. Таким образом окончательное преобразование координат ВО - произведение всех матриц преобразований его предков. Была идея вообще любые афинные преобразования позволять, но передумал.
3. Для каждого ВО введены настройки визуализации - толщина, цвет линии, заливки и т.д.
4. Документ может быть сохранен в файл или стрим тремя разными способами: запись метафайла, запись битмапа, запись объектов (для этого способа предусмотрено и чтение). В последнем случае используется персистентность, ВО сами описывают что должно быть записано в поток, а то, каким образом это будет сделано определяет менеджер записи. Написаны два стандартных менеджера для бинарного формата и XML.
5. Код, отвечающий за обработку пользовательского ввода и вывода документа в контейнере вынесен в отдельные объекты-менеджеры ввода и вывода. Контейнер, ссылаясь на различные менеджеры, может вести себя по-разному, например, может реализовать рабочую область приложения, или область для предварительного просмотра печати как всего документа так и отдельных листов.
6. Различного рода оптимизации, главная из которых - при изменении в документе обновляется не весь контейнер, а только область, которую изменения затронули. |
|
12-02-2009 07:30о, все понял..sorry за беспокойство и большое спасибо за статью, очень полезно. |
|
12-02-2009 04:39Что то я никак не пойму - каким образом задается, что Контейнер должен иметь полосы прокрутки, или все потомки CustomControl их имеют? Тогда вопрос - а как их отключить или хотя бы не показывать, чтобы реализовать перемещение по контейнеру с помощью мыши?
и наверное еще один глупый вопрос - не могу понять что именно значат FOrgin'ы смещение видимой части относительно "начала координат") и LeftCol, TopRow? |
|
23-01-2009 15:56Есть некие наследники от TBaseVisualObject, положение которых по вертикали (т.е. - Тор) должно быть одинаковым для всех объектов этого класса, как бы я ни таскал мышью отдельные его экземпляры. ...
Мне сложно представить ситуацию, при которой объекты TBaseVisualObject (или его наследники) были бы связаны по "классовому признаку".
Мне кажется, "идеологически верно" связывать объекты по какому-либо признаку предметной области, например, принадлежность объектов одному уровню иерархии в отношении между объектами.
Тогда возникает задача установления отношений между объектами TBaseVisualObject (или его наследниками): каждый подчиненный объект регистрирует себя у более "старшего" объекта (на это может указывать объект типа "линия связи"); или объекты объединяются в некий "равноправный" кластер, который ведет себя одинаково.
Тогда при перетаскивании этого старшего объекта, он рассылает сообщение "изменение положения" всем зарегистрировавшимся у него младшим объектам; или при перетаскивании "младшего" объекта, "старший" рассылает подобное сообщение остальным младшим объектам.
|
|
22-01-2009 07:06сообщение от автора материала Ааа, прошу прощения, я кажется несовсем правильно понял Ваш вопрос... Нужно подумать |
|
22-01-2009 04:57сообщение от автора материала Может, кто-нибудь подскажет что-то более "идеологически выдержанное"?
Перекрыть Get(Set)Vertex таким образом, чтобы Get при определенном индексе возвращал фиксированное по Y значение, а Set при этом индексе устанавливал только X ;-) |
|
21-01-2009 14:40 Пытаюсь реализовать следующую функциональность:
Есть некие наследники от TBaseVisualObject, положение которых по вертикали (т.е. - Тор) должно быть одинаковым для всех объектов этого класса, как бы я ни таскал мышью отдельные его экземпляры.
Ничего лучше такой схемы придумать не смог: объект генерирует событие "изменение положения", Контейнер его отлавливает и далее рассылает сообщение (или просто присваивает значение соотв. св-ву) всем объектам этого класса.
Может, кто-нибудь подскажет что-то более "идеологически выдержанное"?
|
|
20-01-2009 02:51сообщение от автора материала Блеск! Но, есть пожелание-замечание. Где персистентность?
Спасибо! И за замечание - тоже. Про персистентность - это Вы верно подметили :) На правах оправдания: мне показалось, что статья уже и так достаточно объемная, а персистентность тема широкая. В результате имеем две статьи вместо одной :) |
|
20-01-2009 00:29 Блеск! Но, есть пожелание-замечание. Где персистентность? Хотелось бы видеть, ведь для недалеко продвинутых - навроде меня - это было бы очень поучительно. Тем более, учитывая интерес автора к этой теме (вспоминаю его статью "Delphi и персистентность — новый взгляд"). |
|
07-01-2009 14:51каким образом данный компонент можно создавать в runtime?
Точно так же, как и другие компоненты, здесь никакого принципиального отличия нет.
http://www.delphikingdom.com/asp/viewitem.asp?catalogid=342
Наверное, я не понял вопроса и решил что речь идет не о Визуальном Контейнере, а об Объектах для Контейнера. |
|
07-01-2009 14:45каким образом данный компонент можно создавать в runtime?
Точно так же, как и другие компоненты, здесь никакого принципиального отличия нет.
http://www.delphikingdom.com/asp/viewitem.asp?catalogid=342
Я считаю что здесь случай чуть другой, вот что у меня получилось:
procedure TForm1.ToolButton11Click(Sender: TObject);
var
Pos: TFloatPoint;
begin
VisualContainer1.ObjectType := TActionBlock;
VisualContainer1.StartConstruct;
TActionBlock(VisualContainer1.SelectedObject).Text := 'Block1';
VisualContainer1.ScreenToLog(100, 100, Pos.X, Pos.Y);
VisualContainer1.SelectedObject.SendCommand(VOC_CONSTRUCTPOINT, 0, Longint(@Pos));
VisualContainer1.ScreenToLog(200, 120, Pos.X, Pos.Y);
VisualContainer1.SelectedObject.SendCommand(VOC_CONSTRUCTPOINT, 0, Longint(@Pos));
VisualContainer1.FinishConstruct;
VisualContainer1.ObjectType := TActionBlock;
VisualContainer1.StartConstruct;
TActionBlock(VisualContainer1.SelectedObject).Text := 'Block2';
VisualContainer1.ScreenToLog(220, 100, Pos.X, Pos.Y);
VisualContainer1.SelectedObject.SendCommand(VOC_CONSTRUCTPOINT, 0, Longint(@Pos));
VisualContainer1.ScreenToLog(320, 120, Pos.X, Pos.Y);
VisualContainer1.SelectedObject.SendCommand(VOC_CONSTRUCTPOINT, 0, Longint(@Pos));
VisualContainer1.FinishConstruct;
end;
|
|
04-01-2009 14:18 На самом деле, ответ лежит там же, т.е. в исходниках. Единственное, чем утешаюсь, так это тем, что уже извинился... ;) Хэппи нью е! |
|
03-01-2009 07:16 Не, я конечно понимаю, что в данной реализации это не нужно, но я хочу прояснить этот момент для себя. Спасибо. |
|
03-01-2009 07:14 Извиняюсь за свою глупость, но кактус буду кусать. Объясните, пож-та, такой момент:
...........
function TVisualContainer.FindObject(X, Y: Integer; var HitTest: Cardinal): Integer;
...
HitTest := TBaseVisualObject(FObjects[i]).SendCommand(VOC_HITTEST,
Longint(Self as ICoordConvert), Longint(@Params));
...
...........
procedure TBaseVisualObject.VOCHitTest(var Command: TVOCHitTest);
begin
Command.Result := HT_OUT;
end;
Насколько я понимаю, в процедуре TBaseVisualObject.VOCHitTest можно при желании как-то использовать Longint(Self as ICoordConvert) для обращения к ICoordConvert. Но как? Что-то этот момент вызвал у меня затруднения. Вроде бы понимаю, что имеем адрес, но как в данном случае можно было бы обратиться к соотв. интерфейсу? |
|
27-10-2008 07:02ё-мое, ну надо же сначала потратить неделю на написание того же самого примерно, а потом натолкнуться на такую грамотную статью.
делал как Geo, через TGraphicControl, хотя знал, что придется переделывать.
сижу читаю, замечательная статья. |
|
23-09-2008 01:34
22-09-2008 13:41У меня такой вопрос, каким образом данный компонент можно создавать в runtime? Нужно это мне так как пытаюсь разобраться с данным примером в Turbo Delphi 2006 (бесплатный), а он не поддерживает сторонние компоненты. |
|
20-05-2008 05:35
19-05-2008 08:42сообщение от автора материала Игорь
Интересно, а можно использовать в качестве подложки графическое изображение? Как сделать прозрачный фон под вновь создаваемыми объектами?
А вы как делаете объекты и контейнер, так же, как в статье описано? Если да, то добавить изображение к контейнеру можно таким образом: определить свойство Picture у класса-контейнера, написать его реализацию, а в методе Paint вставить код, рисующий это изображение перед отрисовкой элементов. Ну а прозрачный фон у объектов совсем просто - это у класса TLogicalCanvas в методах DrawRect и пр. вместо
FCanvas.Brush.Style := bsSolid;
писать
FCanvas.Brush.Style := bsClear;
|
|
19-05-2008 08:05Здравствуйте!
Хорошая поучительная статья, спасибо.
Интересно, а можно использовать в качестве подложки графическое изображение? Как сделать прозрачный фон под вновь создаваемыми объектами? |
|
08-05-2008 05:16Спасибо Автору! Существенно помогло в реализации моего проекта! ^_^ |
|
23-04-2008 12:34сообщение от автора материала Я предпочитал идти другим путем. Наследование фигур от TGraphicControl и тесное взаимодействие абстрактного базового класса фигуры с подложкой...
Понятно, я так тоже делал некоторое время. Но вот именно универсальностью мне жертвовать и не хотелось бы. Это ведь такая вещь, сегодня у тебя один проект, завтра - другой, но задачи могут быть сходны по классу и хотелось бы использовать старые наработки, а не изобретать все заново. А достигается универсальность именно максимальной степенью абстракции.
Честно говоря, в приведенной реализации абстракция далека от максимальной. Ведь и контейнер можно было бы сделать самодостаточным, чтобы его практически (или полностью) без изменений можно было бы использовать в другой задаче. А для этого следовало бы "отвязать" от контейнера трансляцию пользовательского ввода в команды объектов. Чтобы в другой задаче, например, можно было бы выполнять клавиатурой и мышью совсем другие операции (так как и объекты могут быть совершенно другими). Отвязать и переложить на другую сущность - тот самый контроллер. Контейнер, получая ввод от пользователя, перенаправляет его контроллеру, а тот уже отправляет команды объектам. Таким образом, в другой задаче мы могли бы просто заменить контроллер, реализовать свой конкретные типы объектов от имеющегося базового класса, а контейнер и вовсе оставить без изменений. Это в идеале, разумеется. |
|
23-04-2008 10:42Прочитал. Интересно, поучительно, полезно.
От моего подхода отличается. Мой тезка делал на максимально абстрактном уровне, наследуясь непосредственно от TObject. Причем сделал объекты максимально самодостаточными, что позволяет работать с ними на чем угодно (собственно, объект для размещения фигурок не рассматривается).
Я предпочитал идти другим путем. Наследование фигур от TGraphicControl и тесное взаимодействие абстрактного базового класса фигуры с подложкой, что позволяло (как мне кажется) эффективнее реализовать редактирование схем за счет распределения функционала между объектом и подложкой. Хотя, возможно, я несколько терял в универсальности и качестве перерисовки.
Впрочем, сравнивать трудно, так как у меня редакторы схем были достаточно простыми, по сравнению с приведенным в статье, но зато нагруженными дополнительным прикладным функционалом.
Но в любом случае, статья хороша. |
|
18-04-2008 08:58>>> Наверное, профессиональное программистское образование
Думаю, что просто хорошее образование. Плюс практика. Плюс умение и желание работать.
Как и Fisher никак не могу даже дочитать до конца. Только начну, что-то сваоивается. Прочитаю обязательно, так как сам решал аналогичные задачи. И хочется сравнить.
Но за одно только вступление про декомпозицию задачи и объяснение, зачем она нужна, уже можно ставить пять баллов ;-)
Если и все остальное не хуже (а, скорее всего, так оно и есть), то кажется в нашем оффтопике появится новый пункт, куда будут отправляться все желающие построить редактор схем или диаграмм, но не знающие, как это делается. |
|
18-04-2008 08:13К сожалению, глубоко вникнуть пока времени нет, но выглядит замечательно. Обязательно вернусь к этому позже. |
|
18-04-2008 08:01Автор вообще молодец для своего возраста :-))) Наверное, профессиональное программистское образование. |
|
18-04-2008 00:00Автор - молодец. Очень грамотная статья и довольно технологичная. Поболее таких статей бы. |
|
|
|