Добрый день! Необходимо написать графический редактор следующего вида: Окно разбито на 2 части, в левой вводятся параметры фигуры, в правой она отрисовывается. Например: для начала задан прямоугольник высотой 50 и шириной 100. Указываем высоту - 25, на ней ширина 50 и фигура должна преобразоваться, приняв форму песочных часов и далее в этом духе. Надеюсь нормально объяснил...
Программу еще не начал, код писать не прошу, необходима консультация в том, какие элементы выбрать для разработки, TPaintBox или TImage и т.д. и как лучше всё это отрисовывать, может кто может дать совет?
TarasBer
21.01.2011 22:30
Что-то я пока не очень понял. Если мы можем только менять ширину на конкретной высоте. То есть фигура может иметь только такой вид?
Гость
21.01.2011 23:51
Да, только такой, возможно надо будет сругленные углы добавить, но пока только такой вид! Может есть у кого какие соображения? Опыт подсказывает, что первое попавшееся решение потом приходится переделывать, хотелось бы узнать мнение знающих людей и приступить к работе!
TarasBer
22.01.2011 0:38
То есть все инструкции определяются только двумя параметрами - высота и ширина? Ну так и храни массив пар (высота, ширина) Изначально массив состоит из 2 пар: (25, 100) и (-25, 100) Каждая команда добавляет пару к массиву. Потом отсортируй по высоте. Потом просто выведи ломаную линию (стандартная функция, передай в неё массив пар (ширина пополам плюс середина экрана, высота)), отрази её (передай пары (середина экрана минус ширина пополам, высота)). Потом нарисуй верхнюю и нижнюю грани (длину линии определи по ширине на максимальной и минимальной высоте).
RussoTuristo
4.02.2011 14:51
Извините, придется заходить не под собой, мой профиль исчез куда-то)))) Навоял что-то, но работает оно совсем не так... Отрисовывается что-то непонятное, а из-за того, что изображение строю в буфере, отследить по шагам не удается... Может я что не так делаю? Подскажите, пожалуйста... Код:
procedure TForm1.BitBtn1Click(Sender: TObject); var i:integer; a1,a2:real; begin //ar1 и ar2-содержат точки аттриума(1-левая грань,2-правая) img.Canvas.Pen.Color:=clBlack; l:=StrtoInt(edit1.text); h:=StrtoInt(edit2.text); //Начальная фигура -прямоугольник(зададим углы) ar1[1,1]:=10; ar1[2,1]:=10; ar1[1,100]:=10; ar1[2,100]:=10+h; ar2[1,1]:=10+l; ar2[2,1]:=10; ar2[1,100]:=10+l; ar2[2,100]:=10+h; //заполним массивы точками из таблицы for i:=2 to 100 do begin if (StringGrid1.Cells[1, i]<>'') and (StringGrid1.Cells[2, i]<>'') then begin a1:=StrToInt(StringGrid1.Cells[1, i]); a2:=StrToInt(StringGrid1.Cells[2, i]); ar1[1,i]:=10+Round((h-a1)/2); ar1[2,i]:=10+a2; ar2[1,i]:=10+Round((h-a1)/2)+a1; ar2[2,i]:=10+a2; end; end; //Тут надо отсортировать массивы ar1 и ar2 по высоте
//Печать того, что получилось img.Canvas.MoveTo(10,10); for i:=1 to 100 do begin if (ar1[1, i]<>0) and (ar1[2, i]<>0) then begin img.Canvas.LineTo(Round(ar1[1,i]),Round(ar1[2,i])); end; end; img.Canvas.MoveTo(10,10+l); for i:=1 to 100 do begin if (ar2[1, i]<>0) and (ar2[2, i]<>0) then begin img.Canvas.LineTo(Round(ar2[1,i]),Round(ar2[2,i])); end; end;
//Отрисовка верхней и нижней граней img.Canvas.MoveTo(10,10); img.Canvas.LineTo(10+l,10); img.Canvas.MoveTo(10,10+l); img.Canvas.LineTo(10+h,10+l); //Перевод изображения из буфера paintbox1.Canvas.CopyRect(bounds(0,0,img.Width,img.Height), img.Canvas,bounds(0,0,img.Width,img.Height)); end;
end.
Массивы ar1 и ar2 из StringGrid по-моему он не заполняет... или потом не считывает....
> img.Canvas.MoveTo(10,10); > for i:=1 to 100 do begin > if (ar1[1, i]<>0) and (ar1[2, i]<>0) then begin > img.Canvas.LineTo(Round(ar1[1,i]),Round(ar1[2,i])); > end; > end; > img.Canvas.MoveTo(10,10+l); > for i:=1 to 100 do begin > if (ar2[1, i]<>0) and (ar2[2, i]<>0) then begin > img.Canvas.LineTo(Round(ar2[1,i]),Round(ar2[2,i])); > end; > end;
> 1 2
У тебя нумерация сбилась.
RussoTuristo
4.02.2011 15:25
Что-то до меня не доходит... В строке 0 - заголовочная часть, её пропускаем, часть StringGrida заполнена, она начинается со строки 1 и считываем для отрисовки соответственно со строки 1... Не понимаю, где ошибся. Объясните, пожалуйста, еще немного...
TarasBer
4.02.2011 15:30
Я тебе привёл 2 фрагмента. В одном ты берёшь 1й индекс 0 и 1 В другом ты берёшь 1й индекс 1 и 2.
Кстати, 1й индекс - это точно номер столбца?
RussoTuristo
4.02.2011 15:52
В одном ты берёшь 1й индекс 0 и 1 В другом ты берёшь 1й индекс 1 и 2. Структуры-то разные по сути, StringGrid и массивы ar1. ar2, если ошибаюсь и здесь ошибка - извините заранее...
А насчет индекса навёл много сомнений, только с массивами я работал не так основательно, что-то сам вникнуть не могу, по моей какой-то логике и примеру из учебника вроде так, но буду благодарен, если вы меня разубедите ...
> Структуры-то разные по сути, StringGrid и массивы ar1. ar2
Нет, ты и там и там обращаешься к "массиву" StringGrid.Cells.
RussoTuristo
4.02.2011 16:11
Спасибо, понял, что с индексами не так!
volvo
4.02.2011 16:36
Цитата
придется заходить не под собой, мой профиль исчез куда-то))))
Ничего не исчезло. Посмотри первое сообщение. Если б профиль исчез, там было бы написано, что сообщение оставил Гость. Пароль забыл, что-ли?
RussoTuristo
4.02.2011 16:46
Цитата(volvo @ 4.02.2011 12:36)
Ничего не исчезло. Посмотри первое сообщение. Если б профиль исчез, там было бы написано, что сообщение оставил Гость. Пароль забыл, что-ли?
Есть подозрения, что меня взломали, пароль не пашет. Копировал имя из этого поста и просил отправить пароль на ящик, написано было, что такого имени нет на форуме, странно...
Чтоб много сообщений не оставлять, спрошу здесь: Все заработало как надо, отрисовывается, но появился ламерский вопрос: как имитировать нажатие кнопки BitBtn? Раньше знал, сейчас даже в drkb найти не могу...
TarasBer
4.02.2011 17:00
Просто пишешь BitBtn1Click(Sender);
RussoTuristo
4.02.2011 17:40
Помогите, пожалуйста, еще с одной вещью: как правильно отсортировать двумерный массив по одному из индексов. Мне нужно расположить значения массивов ar1 и ar2 в порядке возрастания по высоте (2-й столбец). Но это оказывается непросто:
for i:=1 to 100 do for j:=1 to 100 do if ar1[2,i]<ar1[2,j] then begin buf1:=ar1[1,i]; buf2:=ar1[2,i]; ar1[1,j]:=ar1[1,i]; ar1[2,j]:=ar1[2,i]; ar1[1,i]:=buf1; ar1[2,i]:=buf2; end;
Что-то пытаюсь придумать, но чушь какая-то выходит....
Спасибо! Невнимательность моя... вроде могу сообразить принцип, а мелочи, даже очень значительные, пропускаю...
Можно узнать, а есть ли в Delphi возмоожность подогнать изображение под холст, то есть, например: на форме есть место под PaintBox размером 200 на 400 пикселей, а изображение может быть очень большим, можно ли как-нибудь "подогнать" его, чтобы оно оставшись своих размеров, отображалось в уменьшенном виде?
TarasBer
4.02.2011 18:37
PaintBox1.Canvas.StretchDraw(какие-то параметры, не помню, дельфи сама подскажет)
RussoTuristo
7.02.2011 12:45
StretchDraw не подойдет на сколько я понимаю.... Paintbox не имеет свойства Graphic... Paintbox1.Canvas.StretchDraw(MyRect,Paintbox1.Graphic); выдает ошибку... Может есть другие способы, или необходимо Painbox менять на что-то?
И еще вопросик... Изображение получается недолговечным... перетаскиваешь какое-нибудь окно или просто сворачиваешь и нету ничего.... как сделать чтобы изображение не стиралось?
А чтобы изображение было "долговечным", и его не надо было перерисовывать каждый раз по OnPaint, используй TImage, он "хранит" свое содержимое...
RussoTuristo
7.02.2011 13:26
Извините за Canvas, действительно пропустил, но Paintbox1.Canvas.Graphic тоже не работает....
То есть чтобы оно "хранилось" вместо Paintbox необходимо использовать Image? Еще раз извините за глупый вопрос, просто не хочется из-за недопонимания переписывать всё...
volvo
7.02.2011 13:49
TPaintBox так устроен, что не хранит изображение, которое в нем нарисовано. Ему просто негде его хранить. Это обычная канва. Чем-то напоминает "узоры на песке", их тоже видно, пока ветер не подует или пока дождь не пойдет. Пока твое окно не перекрыто другими окнами - все видно. Как только окно перекроется - все, что было тобой нарисовано в PaintBox-е сотрется. Чтобы восстановить изображение, тебе придется перерисовать его. Поэтому заполняют TPaintBox обычно в событии OnPaint, то есть, полностью перерисовывают содержимое каждый раз, когда приходит сообщение WM_PAINT.
TImage снимает с тебя заботу о перерисовке своего содержимого. Допустим, по нажатию TButton ты нарисовал линию, свернул программу на таскбар, развернул - линия ровно в том же месте, где и была. PaintBox в аналогичном случае будет пуст.
RussoTuristo
7.02.2011 13:56
Спасибо за разъяснение, запомню и переделаю, в принципе не так много работы прибавится.
TarasBer
7.02.2011 14:29
> Извините за Canvas, действительно пропустил, но Paintbox1.Canvas.Graphic тоже не работает....
Просто Canvas, блин, без .Graphic!
RussoTuristo
7.02.2011 20:37
Вместо Paintbox взял Image, всё отрисовывается хорошо, не исчезает никуда, но StretchDraw упрямится - ошибок не выдает но и не сжимает ничего, когда изображение больше, чем Image, видна лишь часть изображения...
и еще вопросик: Изображение не пропадает, но не делает это вообще: Пишу в событии кнопки первой строкой Img.free и выдает ужасную ошибку: Invalide Pointer operation
TarasBer
7.02.2011 20:57
А ты задай MyRect побольше (или поменьше) раз в 10, узнаешь, напутал, или нет. А лучше задавай MyRect в зависимости от максимального и минимального из вводимых значений.
RussoTuristo
7.02.2011 21:19
Действительно, ошибок не выдает, но это не значит, что работает... думаю я не в том месте его указываю(хотя пробовал в разных) или аргументы не те, посмотрите, пожалуйста, может какие замечания будут по стилю или применению функций, новичок в этом деле, готов учиться на своих ошибках...
procedure TForm1.BitBtn1Click(Sender: TObject); var i,j:integer; a1,a2,buf1,buf2:real; MyRect:TRect; begin //ar1 и ar2-содержат точки фигуры(1-левая грань,2-правая) img.Canvas.Pen.Color:=clBlack; l:=StrtoInt(edit1.text); h:=StrtoInt(edit2.text); //Начальная фигура -прямоугольник(зададим углы) ar1[1,1]:=10; ar1[2,1]:=10; ar1[1,100]:=10; ar1[2,100]:=10+h; ar2[1,1]:=10+l; ar2[2,1]:=10; ar2[1,100]:=10+l; ar2[2,100]:=10+h; //заполним массивы точками из таблицы for i:=1 to 100 do begin if (StringGrid1.Cells[0, i]<>'') and (StringGrid1.Cells[1, i]<>'') then begin a1:=StrToInt(StringGrid1.Cells[0, i]); a2:=StrToInt(StringGrid1.Cells[1, i]); ar1[1,i+1]:=10+Round((l-a1)/2); ar1[2,i+1]:=10+a2; ar2[1,i+1]:=10+Round((l-a1)/2)+a1; ar2[2,i+1]:=10+a2; end; end; //Сортируем массивы по высоте for i:=1 to 100 do for j:=1 to 100 do if ar1[2,i]<ar1[2,j] then begin buf1:=ar1[1,j]; buf2:=ar1[2,j]; ar1[1,j]:=ar1[1,i]; ar1[2,j]:=ar1[2,i]; ar1[1,i]:=buf1; ar1[2,i]:=buf2; end; for i:=1 to 100 do for j:=1 to 100 do if ar2[2,i]<ar2[2,j] then begin buf1:=ar2[1,j]; buf2:=ar2[2,j]; ar2[1,j]:=ar2[1,i]; ar2[2,j]:=ar2[2,i]; ar2[1,i]:=buf1; ar2[2,i]:=buf2; end; //Печать того, что получилось img.Canvas.MoveTo(10,10); for i:=1 to 100 do begin if (ar1[1, i]<>0) and (ar1[2, i]<>0) then begin img.Canvas.LineTo(Round(ar1[1,i]),Round(ar1[2,i])); end; end; img.Canvas.MoveTo(10+l,10); for i:=1 to 100 do begin if (ar2[1, i]<>0) and (ar2[2, i]<>0) then begin img.Canvas.LineTo(Round(ar2[1,i]),Round(ar2[2,i])); end; end; //Отрисовка верхней и нижней граней img.Canvas.MoveTo(10,10); img.Canvas.LineTo(10+l,10); img.Canvas.MoveTo(10,10+h); img.Canvas.LineTo(10+l,10+h); //Перевод изображения из буфера MyRect := Rect(0,0,Image1.ClientWidth,Image1.ClientHeight); Image1.Canvas.StretchDraw(MyRect, Image1.Picture.Graphic); Image1.Canvas.CopyRect(bounds(0,0,img.Width,img.Height), img.Canvas,bounds(0,0,img.Width,img.Height)); end;
Без StretchDraw никак, а он не пашет...
TarasBer
7.02.2011 21:46
Кто так сортировку пишет? У тебя она неправильно сортирует вообще. Видимо, ты откуда-то переписал, причём очень небрежно и не глядя, что там происходит.
for i:=1 to 100 do for j:=i+1 to 100 do
Дальше, а с чего StretchBlt будет работать, если у тебя и буфер, и область вывода, и прямоугольник, который ты выводишь - одного размера? Чему там и куда сжиматься? И зачем ты копируешь из себя в себя?
Почему фигура у тебя выводится от 10, если должна рисоваться от середины буфера (от img.Width div 2)?
ar1[1,1]:=img.Width div 2; и так далее надо
RussoTuristo
7.02.2011 22:00
Насчет изображения понял... Почему-то такие глобальные ошибки сам не замечаешь, путаясь в мелочах, я рисовал от кромки, а чтобы она не была вплотную, сделал отступ в 10 пикселей, переделаю, чтобы всё от середины плясало, поэтому в принципе и сжать-то невозможно было... Спасибо огромное, переделаю, напишу.
А по поводу цикла - это эксперимент был и он удался, если сделать как ты говоришь, то появляются какие-то левые 2 линии по бокам, даже не знаю откуда, но тоже попробую привести к человеческому виду. Еще раз спасибо, будем работать!
RussoTuristo
8.02.2011 12:54
Помогите, пожалуйста, с удалением изображения, Img.Destroy напрочь отказывается работать, всё та же ошибка - Invalid Pointer Operation. По идее Create - Destroy, но что-то не так тут...
TarasBer
8.02.2011 14:27
Create-Free, а не Create-Destroy.
Опять невнимательность!
RussoTuristo
8.02.2011 14:51
Free тоже самое выдает, я пробовал....
TarasBer
8.02.2011 15:13
Покажи, как ты этот Free вызываешь?
RussoTuristo
8.02.2011 15:32
img.free; первым делом при нажатии кнопки... Пробовал одновременно с этим буфер очищать, но тогда вообще ошибка сразу с нулями вылетает.
TarasBer
8.02.2011 15:38
Ты создаёшь img при создании формы, а уничтожаешь при нажатии кнопки. Где логика. В OnDestroy деструкторы перенеси.
RussoTuristo
9.02.2011 14:06
Форма-то заново не создается и не уничтожается, деструкторы походу оттуда не работают, поэтому и помещал их в кнопку, т.к. в момент нажатия они должны уничтожаться:
procedure TForm1.FormDestroy(Sender: TObject); begin buffer.Free; img.Free; end;
Без ошибок и без результатов.
и вопросик по поводу StretchDraw:
Моя логика, решил не строить от середины, почему-то не удобно: Создаем в памяти изображение нужной ширины+50, и высоты+50: // 50 отведено на небольшие подписи
Это что, это зачем? Зачем копировать ещё раз? Ты сначала зачем-то вывел содержимое Image1 на себя со сжатием, а потом без сжатия туда же скопировал буфер.
Я же тебе сказал, как правильно выводить. А ты забил и сделал свою хрень неправильно. У тебя полное непонимание того, что ты делаешь.
RussoTuristo
11.02.2011 15:37
Цитата
Какой результат тебе нужен?
Чтобы при нажатии кнопки BitBtn1 старое изображение уничтожалось, а не при уничтожении формы
Цитата
Я же тебе сказал, как правильно выводить. А ты забил и сделал свою хрень неправильно.
Stretchdraw ожидает в качестве параметра тип .Graphic, img.Canvas такого свойства не имеет, поэтому как туда вставить его я не знаю... в этом и проблема основная.
Вообще уже не знаю что делать, весь мозг сломал, помогите, пожалуйста, переделать.
TarasBer
11.02.2011 15:44
> Чтобы при нажатии кнопки BitBtn1 старое изображение уничтожалось, а не при уничтожении формы
Хорошо. А создавать новое изображение тогда надо когда?
> Stretchdraw ожидает в качестве параметра тип .Graphic, img.Canvas такого свойства не имеет
Тебе нужен тип или свойство? Я же тебе написал, как надо использовать StretchDraw! Ищи сам в этой теме.
RussoTuristo
11.02.2011 15:50
Цитата
Image1.Canvas.StretchDraw(MyRect, Img);
Я так понимаю, речь идёт об этой строке, но изображение всё равно не сжимается, а часть его остается где-то за пределами....
Цитата
Хорошо. А создавать новое изображение тогда надо когда?
При нажатии кнопки первым делом удаляем старое, и строим новое. Делаю так, потому что форма не уничтожается в процессе работы.
TarasBer
11.02.2011 16:08
> Я так понимаю, речь идёт об этой строке, но изображение всё равно не сжимается, а часть его остается где-то за пределами....
Да, об этой. Она именно выводит то, что было в Img на форму. То, что там что-то за пределами - ну так что было в Img, то и вывелось. Можешь написать для контроля Img.SvaeToFile() и сравнить.
> При нажатии кнопки первым делом удаляем старое, и строим новое.
Что-то я не увидел, чтобы ты создавал новое при нажатии кнопки. У тебя, видимо полное непонимание происходящего.
TBitmap.Create - это создание изображения для работы, если этого не сделать, то с ним ничего делать нельзя. Эта процедура соответствует доставанию нового листа бумаги из стола. TBitmap.Free - это не стереть изображение ластиком с листа! Это выкинуть лист в мусорку, потому что тебе он больше не нужен. После Free делать с изображением ничего нельзя, поэтому его лучше пропиши в OnDestroy.
Всякие там рисования - это не создание нового листа, это рисование на текущем. А для того, чтобы стереть изображение ластиком, делай так:
with Img.Canvas do begin Brush.Style := bsSolid; // стиль заливки - заливать по полной Brush.Color := clWhite; // цвет заливки - белый Pen.Style := psClear; // линии не рисовать Rectangle(0, 0, Width, Height); // нарисовать прямоугольник на всю область end;
А потом, перед рисованием, не забудь обратно написать Pen.Style := psSolid; (а потом он линии рисовать не будет).
RussoTuristo
12.02.2011 21:08
Вроде доработал все, изображение строится, сжимается/растягивается, но как-то иногда очень криво, что-то не прорисовывается, когда изображение большое - иногда целые линии не видны. Увеличил размер кисти, стало лучше, даже нормально, но это, наверное, не правильный способ.... Можно как-то по-другому это реализовать - улучшить качество?
И еще вопрос, добавил панель для расчетов, поместил Editы, туда ввожу значения, но при расчете вылетает ошибка: "EConverError with message "" is not a vali floating point value". Незаполненных Editов нету, странно... Хотя типы должны совпадать:
Qn, Vlin,r:real; tau:integer;
Procedure Init; begin Qn:=StrToFloat(Edit3.text); Vlin:=StrToFloat(Edit4.Text); tau:=StrToInt(Edit7.Text); r:=Vlin*tau; Edit8.text:=FloatToStr®; end;
-TarasBer-
12.02.2011 21:25
> о при расчете вылетает ошибка: "EConverError with message "" is not a vali floating point value".
Это не значит, что он не заполнен. Это значит, что его содержимое - не число. Кстати, в качестве разделителя нужна именно запятая (кажется).
> что-то не прорисовывается, когда изображение большое - иногда целые линии не видны
Функцию сжатия писал Микрософт. Видать, хреново написал. У меня тоже при сжатии вместо оттенков серого просто пропадают линии, даже если принудительно задать режим сжатия halftone .
> Можно как-то по-другому это реализовать - улучшить качество?
Да. Изначально задать размер буфера, совпадающий с размером выводимого участко и при рисовании в буфер использовать только относительные координаты, никаких точных чисел. То есть делать так: w := Img.Width; h := Img.Height; ... LineTo(round(w * 0.1), round(h * 0.34));
(для первой координаты через w, для второй через h).
RussoTuristo
12.02.2011 21:51
В поля text у Edit3, Edit4 и Edit7 введены значения 1380, 10 и 10 соответственно без запятых и без пробелов, поле для вывода - пустое. Вообще когда добавил 2-ю панель она как не родная))) С первой панели из такого же Edita считывается, а эта панель вообще мёртвая почему-то.... Хотя они по сути идентичные.
-TarasBer-
12.02.2011 22:24
> В поля text у Edit3, Edit4 и Edit7
Ты хоть сам помнишь, что они означают? Возьми редактор свойств объектов и исправь им значение поля Name на нормальное.
Да, пустое поле тоже вызывает ошибку при попытке перевести в число. Кстати, я в таких случаях делаю примерно так:
function ToFloat(E: TEdit): extended; var c: integer; begin val(E.Caption, Result, c);// пытаемся перевести чесло в строку if c <> 0 then begin // если код ошибки не равен нулю ShowMessage(E.Caption + " не число!!!"); E.SelectText(1, Length(E.Caption)); // как-то так, не помню, там вроде два свойства надо менять для выделения текста end; end;
Спасибо за функцию, думаю пригодится в будущем, только прибавить к ней немного кода на запрет значений, кроме числовых и ","... и вместо caption, наверное, text, а-то что-то не сходится....
Даже так не работает, когда вероятность попадания чего-то ненужного исключена.
volvo
13.02.2011 0:16
Значит, где-то в обработчиках чего-то лишнего понавешал. На пустом проекте прекрасно отработала процедура Init. Прикрепляй свой проект целиком (только не надо EXE-шники и все временные файлы пихать, ладно? Скомпилировать я и сам могу, меня исходники интересуют)
RussoTuristo
13.02.2011 0:37
Вот проект без ЕХЕшников: Заранее спасибо.
volvo
13.02.2011 0:49
OMG...
А ничего, что Edit3 и Form1.Edit3 (и так далее) - это разные вещи? Убери напрочь этот свой "страшный Var" (у тебя ж сейчас все переменные, описанные там - NIL-ы, не инициализированные они), и сделай Init методом TForm1, тогда все будут всё видеть...
TForm1 = class(TForm) // тут ничего не меняй private { Private declarations } public { Public declarations }
procedure Init; // <--- Добавляешь прототип end;
procedure TForm1.Init; // <--- Добавляешь Tform1. begin end;
RussoTuristo
13.02.2011 11:52
Спасибо, уверен был, что там косяк, но не знал куда пихнуть метод Init в заголовочную часть....
Это текстовая версия — только основной контент. Для просмотра полной версии этой страницы, пожалуйста, нажмите сюда.