
![]() |
Наши проекты:
Журнал · Discuz!ML · Wiki · DRKB · Помощь проекту |
|
ПРАВИЛА | FAQ | Помощь | Поиск | Участники | Календарь | Избранное | RSS |
[216.73.216.75] |
![]() |
|
Сообщ.
#1
,
|
|
|
Добрый день.
Помогите решить проблемы! Есть простенькая программа, рисующая круги в PaintBox... И всё вроде бы работало нормально, пока я не заметил, что если какое либо приложение нашло на мою форму то изображение "затирается"... Я поискал ответа на этом форуме, но там всё как то слишком обще сказано и не понятно мне... =) И вторая проблема - сохранение изображения из PaintBox. Поиск помог отрыть кусок кода для сохранения. Вот он: ![]() ![]() void __fastcall paintBoxToFile( TPaintBox * aPaintBox, AnsiString aFileName ){ // Создаем bitmap Graphics::TBitmap * bmp = new Graphics::TBitmap(); try{ // Устанавливаем размеры и формат bmp->PixelFormat = pf24bit; bmp->Height = aPaintBox->ClientHeight; bmp->Width = aPaintBox->ClientWidth; // Запоминаем старый handle HANDLE oldHandle = aPaintBox->Canvas->Handle; try{ // Устанавливаем handle bitmap'а aPaintBox->Canvas->Handle = bmp->Canvas->Handle; // Заставляем вывести изображение // Если не будет обработчика, то на этом месте программа упадет aPaintBox->OnPaint( aPaintBox ); } __finally{ // Восстанавливаем handle aPaintBox->Canvas->Handle = oldHandle; } // Сохраняем в файл bmp->SaveToFile( aFileName ); } __finally{ // Удаляем bitmap delete bmp; } } Но объясните мне как заставить PaintBox вывести изображение? Потому что файл сохраняется, но он пустой... То есть белый. Заранее спасибо! |
Сообщ.
#2
,
|
|
|
Цитата Yura93 @ если какое либо приложение нашло на мою форму то изображение "затирается"... Потому что файл сохраняется, но он пустой... Значит обработчик OnPaint реализован неправильно |
Сообщ.
#3
,
|
|
|
Суть такая:
- создаёшь временный битмап bmp размерами с пайнтбокс - создаёшь некий метод Draw(), который рисует именно на этом битмапе - в событии PaintBox->OnPaint() просто отображаешь этот битмап на канве пайнтбокса PaintBox->Canvas->Draw(0, 0, bmp) - ну а сохранить битмап в файл проблему не составит |
Сообщ.
#4
,
|
|
|
Цитата Сан Иваныч @ Суть такая: ... А какой тогда смысл вообще юзать PaintBox ? Проще заменить его на TImage - рисуешь один раз картинку, при этом автоматом создаться битмап, и перерисовывается автоматически без всяких OnPaint, и об удалении битмапа думать не нужно |
Сообщ.
#5
,
|
|
|
да, как предложил leo лучше сделай. PaintBox постоянно перерисовываеться, а c TImage нарисовал и забыл
![]() |
Сообщ.
#6
,
|
|
|
PaintBox в данном случае имеет смысл, если недолгая отрасовка, и смысла нету картинку держать, или она хранится другим способом, например чараз api или какой-то специфической компонентой, или библиотекой. При этом можно повесить обработчик и рисовать не всю область, а только то, что нуждается в отрисовке при желании. В простом вар-те - да, лучше положить timage и не извращаться.
|
Сообщ.
#7
,
|
|
|
Вы меня не так поняли!
![]() Из всех возможный событий у меня используются только OnMouseDown, OnMouseUp, OnMouseMove. Если ны нажал и водишь мышкой по PaintBox то рисуется след из кругов. Их цвет и размер можно менять. А я хочу чтобы нарисованное сообщение можно было сохранить. И чтобы оно не затиралось другими приложениями. В данном случае Image не подходит потому что у него нет свойство OnMouseMove. И если надо вот кусок кода отвечающий за отрисовку: ![]() ![]() void __fastcall TForm1::PaintBox1MouseMove(TObject *Sender, TShiftState Shift, int X, int Y) { if(MouseD){ Size=StrToInt(Edit1->Text); if(CheckBox3->Checked){ PenW=StrToInt(Edit2->Text); } else{ PenW=0; PenC=Panel2->Color; } PaintBox1->Canvas->Brush->Color=Panel2->Color; PaintBox1->Canvas->Pen->Color=PenC; PaintBox1->Canvas->Pen->Width=PenW; if(RadioGroup1->ItemIndex==0){ PaintBox1->Canvas->Ellipse(X - Size,Y - Size, X + Size, Y + Size); } else{ PaintBox1->Canvas->Rectangle(X - Size,Y - Size, X + Size, Y + Size); } } } |
Сообщ.
#8
,
|
|
|
Цитата Yura93 @ В данном случае Image не подходит потому что у него нет свойство OnMouseMove. У тебя какой Builder ? на 6 ке есть. PS. Можно легко научить его этому Так или так : ![]() ![]() // *.h файл class TForm1 : public TForm { //... public: // User declarations __fastcall TForm1(TComponent* Owner); TWndMethod OldWndProc; void __fastcall NewWndProc(TMessage &Msg); }; // *.cpp файл //--------------------------------------------------------------------------- __fastcall TForm1::TForm1(TComponent* Owner) : TForm(Owner) { OldWndProc = Image1->WindowProc; Image1->WindowProc = NewWndProc; } //--------------------------------------------------------------------------- void __fastcall TForm1::NewWndProc(TMessage &Msg) { POINT p; switch (Msg.Msg) { case WM_MOUSEMOVE : { p.x = Msg.LParamLo; p.y = Msg.LParamHi; } case WM_DESTROY : { Image2->WindowProc = OldWndProc; break; } } OldWndProc(Msg); } |
Сообщ.
#9
,
|
|
|
CodeGear2009
|
Сообщ.
#10
,
|
|
|
Цитата Yura93 @ Вы меня не так поняли! ![]() Из всех возможный событий у меня используются только OnMouseDown, OnMouseUp, OnMouseMove Это ты не так понял назначение TPaintBox, который в отличие от TImage, нигде не хранит нарисованное изображение и поэтому оно должно каждый раз, целиком перерисовываться, и нигде нибудь, а именно в OnPaint. А если рисовать в PaintBox'е не в OnPaint, а по каким-то "левым" событиям, то при любой перерисовке окна, инициированной виндой (при перекрытии, изменении размеров и т.п.) - все твои "художества" потеряются, что собс-но у тебя и происходит Поэтому тебе нужен именно TImage, который сохраняет все, что тобой нарисовано, в своем Picture.Bitmap, и соответственно автоматически отображает этот битмап на экране по виндовым запросам WM_PAINT. И события OnMouse... в TImage должны быть, по крайней мере в BCB 6 они есть, и поэтому твое заявление Цитата Yura93 @ В данном случае Image не подходит потому что у него нет свойство OnMouseMove - скорее всего неверно |
Сообщ.
#11
,
|
|
|
А что должно быть в событии OnPaint?
|
Сообщ.
#12
,
|
|
|
Цитата Yura93 @ А что должно быть в событии OnPaint? то что ты хочешь видеть в своем PainBox'е. |
Сообщ.
#13
,
|
|
|
Цитата popsa @ то что ты хочешь видеть в своем PainBox'е. Ёмко но не понятно... Приведите пример кода... |
Сообщ.
#14
,
|
|
|
к примеру напиши в своем примере в обработчике OnPaint
![]() ![]() ![]() PaintBox1->Canvas->Pen->Color=clRed; PaintBox1->Canvas->Pen->Width=3; PaintBox1->Canvas->Rectangle(40,30, 100, 100); |
Сообщ.
#15
,
|
|
|
Стоп! Когда наступает событие OnPaint?
|
Сообщ.
#16
,
|
|
|
Цитата Yura93 @ Стоп! Когда наступает событие OnPaint? каждый раз, когда происходит перерисовка твоего PaintBox'а. |
Сообщ.
#17
,
|
|
|
Цитата Yura93 @ Стоп! Когда наступает событие OnPaint? Когда винда или твоя прога просят полностью или частично перерисовать прямоугольник, занятый твоим PaintBox'ом. При этом то, что ты рисовал ранее стирается и нужно все рисовать заново. Если дружишь с аглицким, то глянь справку по TPaintBox::OnPaint и особенно Example2 - пример того как делать нельзя, как раз твой случай ![]() Добавлено PS: Говоря "по научному" PaintBox рисует изображение непосредственно на "канве экрана" (screen buffer). Поэтому когда PaintBox перекрывается другим окном, то это место канвы экрана перерисовывается другим окном и старое изображение от твоего PaintBox'а ес-но затирается = пропадает. Соотв-но, когда это окно закрывается\перемещается, твой PaintBox всплывает наверх и винда просит его перерисовать соответсвующую область экрана заново - шлет ему сообщение WM_PAINT, которое вызывает событие OnPaint. Если ты в обработчике OnPaint ничего не нарисуешь, то соотв-но получишь белый прямоугольник. TImage, в отличие от PaintBox'a, рисует изображение не прямо на экране, а в битмапе (по умолчанию) и затем по требованию WM_PAINT просто копирует весь битмап или его часть на экран. Соотв-но, при всяких перекрытиях окон с битмапом ничего не происходит и изображение никуда не пропадает, и никаких OnPaint не нужно, т.к.отрисовка битмапа на экране происходит автоматически |
Сообщ.
#18
,
|
|
|
Цитата Yura93 @ Стоп! Когда наступает событие OnPaint? Когда винде надо отрисовать твоё окно. Причём там есть фишка, что когда приходит onPaint - не обязательно его отрисовывать во всех случаях, т.к. если например будешь окно таскать по экрану - то увидишь, что оно не перерисовывается постоянно, а просто таскается, но если там будет стоять твой обработчик - то ему событие будет приходить и ты будешь перерисовывать. Если делать на api - то там есть такая фишка как begin_paint кажется, вот с помощью связки begin_paint и end_paint - там формировалась структура и с её помошью можно было определить что нужно рисовать и нужно ли, аналогию в VCL я не знаю, но если заметишь, что твоё приложение медленно рисуется при таскании окна и это будет критично, можешь туда копнуть. К сожалению, я на память всё не помню, надо код свой смотреть, чтобы детали рассказать. |
Сообщ.
#19
,
|
|
|
Цитата brother79 @ Причём там есть фишка, что когда приходит onPaint - не обязательно его отрисовывать во всех случаях, т.к. если например будешь окно таскать по экрану - то увидишь, что оно не перерисовывается постоянно, а просто таскается, но если там будет стоять твой обработчик - то ему событие будет приходить и ты будешь перерисовывать Ничего подобного, WM_PAINT и соотв-но OnPaint приходят только тогда, когда задана конкретная область, которую нужно перерисовать. В begin_paint эта область задается прямоугольником lpPaint->rcPaint, в VCL этот прямоугольник = Canvas->ClipRect Но все - это излишние подробности, т.к. TPaintBox, несмотря на свое обманчивое название, совершенно не подходит для "простейшего графического редактора", т.к. он не запоминает нарисованное изображение и для того, чтобы оно не терялось при перерисовках, придется рисовать не сразу на экран, а в битмап и затем копировать этот битмап на экран в OnPaint. Спрашивается - зачем городить огород, если все это уже реализовано в TImage ? |
Сообщ.
#20
,
|
|
|
Я придумал как сохранять изображения! Если что то было нарисовано на PaintBox то тоже самое рисуется и на невидимом Image... Но это не работает при Resize и я не знаю что нужно написать чтобы в OnPaint картинка из Image рисовалась на PaintBox...
|
Сообщ.
#21
,
|
|
|
Цитата Yura93 @ Я придумал как сохранять изображения! ![]() За тебя уже Борланд все давно придумал - юзай TImage вместо TPainBox и не морочь голову, с чего ты взял, что у него нет событий OnMouse.. ? В 6-м билдере точно есть, а у тебя какой ? Добавлено Цитата Yura93 @ что нужно написать чтобы в OnPaint картинка из Image рисовалась на PaintBox... Ну если ты извращенец, то напиши ![]() ![]() PaintBox1->Canvas->Draw(0,0,Image1->Picture->Bitmap); Добавлено PS: События OnMouse есть у всех наследников TControl, как их может не быть у TImage - загадка ![]() |
Сообщ.
#22
,
|
|
|
![]() Добавлено Ах да! Если надо то вот код: ![]() ![]() void __fastcall TForm1::Image1MouseDown(TObject *Sender, TMouseButton Button, TShiftState Shift, int X, int Y) { MouseD=true; } //--------------------------------------------------------------------------- void __fastcall TForm1::Image1MouseUp(TObject *Sender, TMouseButton Button, TShiftState Shift, int X, int Y) { MouseD=false; } //--------------------------------------------------------------------------- void __fastcall TForm1::Image1MouseMove(TObject *Sender, TShiftState Shift, int X, int Y) { Size=StrToInt(Edit1->Text); if(MouseD){ Image1->Canvas->Brush->Color=Panel2->Color; if(CheckBox3->Checked){ Image1->Canvas->Pen->Color=Panel6->Color; Image1->Canvas->Pen->Width=StrToInt(Edit2->Text); } else{ Image1->Canvas->Pen->Color=Panel2->Color; Image1->Canvas->Pen->Width=0; } if(RadioGroup1->ItemIndex==1){ Image1->Canvas->Ellipse(X-Size,Y-Size,X+Size,Y+Size); } else{ Image1->Canvas->Rectangle(X-Size,Y-Size,X+Size,Y+Size); } } } //--------------------------------------------------------------------------- Может кто-нибудь увидит тут серьёзную ошибку? |
Сообщ.
#23
,
|
|
|
Отслеживать MouseDown\Up и устанавливать флаг MouseD никчему, т.к. признак зажатия кнопки передается в параметре Shift обработчика MouseMove
![]() ![]() //if(MouseD){ if (Shift.Contains(ssLeft)) { //перемещение мыши с нажатой левой кнопкой ... |
Сообщ.
#24
,
|
|
|
leo, спасибо я исправил. Но всё равно программа не реагирует не вождение мышью по Image и не приходит туда сколько бы я не кликал и не водил мышью.
Теперь код выглядит так: ![]() ![]() void __fastcall TForm1::Image1MouseMove(TObject *Sender, TShiftState Shift, int X, int Y) { Size=StrToInt(Edit1->Text); if(Shift.Contains(ssLeft)){ Image1->Canvas->Brush->Color=Panel2->Color; if(CheckBox3->Checked){ Image1->Canvas->Pen->Color=Panel6->Color; Image1->Canvas->Pen->Width=StrToInt(Edit2->Text); } else{ Image1->Canvas->Pen->Color=Panel2->Color; Image1->Canvas->Pen->Width=0; } if(RadioGroup1->ItemIndex==1){ Image1->Canvas->Ellipse(X-Size,Y-Size,X+Size,Y+Size); } else{ Image1->Canvas->Rectangle(X-Size,Y-Size,X+Size,Y+Size); } } } |
Сообщ.
#25
,
|
|
|
Цитата Yura93 @ Но всё равно программа не реагирует не вождение мышью по Image и не приходит туда сколько бы я не кликал и не водил мышью Ну, не знаю, у меня нормально реагирует и рисует Может обработчик в инспекторе не присвоил, или неcколько Image создал - присвоил одному, а водишь мышью по другому - казусы разные бывают ![]() |
Сообщ.
#26
,
|
|
|
Цитата leo @ Может обработчик в инспекторе не присвоил Что это значит? |
Сообщ.
#27
,
|
|
|
Так... Ну ошибку я исправил... Почему то глюк был из-за того что Image лежал на Panel а она видимо автоматически кладётся поверх... Но теперь почему то не работает функция формы OnResize... Вот код:
![]() ![]() { void __fastcall TForm1::FormResize(TObject *Sender){ Image1->Width=Form1->ClientHeight-16; Image1->Height=Form1->ClientWidth-133; Image1->Top=8; Image1->Left=8; } //--------------------------------------------------------------------------- Но тут важно то что Image в принципе не меняет своих размеров... |
Сообщ.
#28
,
|
|
|
Цитата Yura93 @ а она видимо автоматически кладётся поверх... ![]() Если все нормально сделано, то без разницы где лежит Image, на форме или на панели Цитата Yura93 @ Но тут важно то что Image в принципе не меняет своих размеров... ![]() Просто размер TImage и размер битмапа это не одно и то же. Размер хранимого изображения определяется размером битмапа Image->Picture->Bitmap. Если битмап создается автоматически при первом рисовании, то его размер устанавливается = размеру Image. В свою очередь размер Image определяет размер видимого изображения на экране. Если Image.AutoSize = false, то после создания битмапа размеры Image и Bitmap не привязаны друг к другу. Поэтому при уменьшении размера Image видимое изображение будет уменьшаться (просто выводится не весь хранимый битмап, а только его часть). А вот при увеличении размера Image видимое изображение ес-но не может быть больше размера самого битмапа (размер рамки Image увеличивается, а размер битмапа остается тем же). Ну а если Image.AutoSize = true, то изменение размеров самого Image вообще не работает (размер = битмапу) и для ресайзинга изображения нужно изменять размер Image->Picture->Bitmap. Поэтому сам думай, как должна вести себя картинка при изменении размеров. Если она должна всегда соответсвовать размеру окна и обрезаться при его уменьшении, то самое просто установить Image.AutoSize = true и в FormResize изменять не размеры Image, а размеры Image->Picture->Bitmap PS: Top и Left при резайзе не изменяются, поэтому незачем их переустанавливать |
Сообщ.
#29
,
|
|
|
Поменял AutoSize на false и всё равно ничего не работает....
|
Сообщ.
#30
,
|
|
|
Цитата Yura93 @ Поменял AutoSize на false и всё равно ничего не работает.... Значит, не судьба ![]() Насчет AutoSize и соотношение размеров Image и Bimap я уже тебе все "разжевал", осталось еще проверить св-ва Align и Constraints. Плюс бросить дурную превычку тыкать мышой и менять значение св-в, не понимая как они работают и на что влияют. Сначала следует почитать справочку или умные книжки, а затем уже тыкать со знанием дела ![]() |
Сообщ.
#31
,
|
|
|
Constraints и Align я не менял! Align = none, Constraints не изменено.
|
Сообщ.
#32
,
|
|
|
В таком случае, конкретно говори, что не работает и приводи код - как говориться "экстрасенсы в отпуске"
![]() Повторяю: если AutoSize = false, то при уменьшении размеров Imаge относительно первоначального, видимое изображение должно уменьшаться, а при увеличении сверх исходного - не должно, т.к. битмап при этом автоматически не увеличивается и его размер остается тем же. Чтобы размер Image и Bitmap всегда совпадали, поставь AutoSize = true и вместо размеров Image изменяй размеры Image->Picture->Bitmap. Все это я уже разжевал в #28, что не понятно ? |
Сообщ.
#33
,
|
|
|
Цитата Yura93 @ Так... Ну ошибку я исправил... Почему то глюк был из-за того что Image лежал на Panel а она видимо автоматически кладётся поверх... Но теперь почему то не работает функция формы OnResize... Вот код: ![]() ![]() { void __fastcall TForm1::FormResize(TObject *Sender){ Image1->Width=Form1->ClientHeight-16; Image1->Height=Form1->ClientWidth-133; Image1->Top=8; Image1->Left=8; } //--------------------------------------------------------------------------- Но тут важно то что Image в принципе не меняет своих размеров... издеваешься? ![]() ![]() { void __fastcall TForm1::FormResize(TObject *Sender){ Image1->Width=Form1->ClientHeight-16; // Width = Height ?? Image1->Height=Form1->ClientWidth-133; // Height = Width ?? Image1->Top=8; Image1->Left=8; } //--------------------------------------------------------------------------- исправь и проверь... у тя Image на все форму? у всех компонентов есть свойство Align. По эксперементируй(alClient) может это то что тебе надо?... |