Форум — Ответы ( К темам )
? | Dr.Phoenix: Как рисовать на форме из вторичного потока? (27-12-2002 16:02:40) |
Моей проге надо часто и много рисовать на форме, если делать это последовательно с другими действиями (напиример она показавает координаты положения курсора), то все остальное начинает заметно тормозить. Если функцию рисования запускать отдельным потоком, то прога вылетает почти сразу и говорит, что форма не разрешает рисование. Почему такое происходит и как это исправить? | |
Devnvd (27-12-2002 20:22:12) http://devnvd.narod.ru | |
Приведу полностью пример рисования на форме в потоке Тонкое место здесь это использование Sleep. Вместо него лучше сделать вертушку PeekMessage<->DispatchMessage или организовывать Event'ы. #include <vcl.h> #pragma hdrstop #include "Unit1.h" //--------------------------------------------------------------------------- #pragma package(smart_init) #pragma resource "*.dfm" TForm1 *Form1; /***************************************************************** Прорисовка в режиме SCROLL картинки ImageBmp в области PaintBox1'а PaintBox вообще-то здесь не к месту, в потоке лучше рисовать на TWinControl. Здесь PaintBox используем только в качестве примера Используются компоненты: TForm *Form1; // Сама форма на которой всё происходит TButton *Button1; //Вызов диалога зарузки картинки TPaintBox *PaintBox1; // Задаёт область рисования на форме TOpenDialog *OpenDialog1; // Для ввода имени файла BMP-картинки TEdit *Edit1; // Для изменения величины таймаута ******************************************************************/ Graphics::TBitmap *ImageBmp=NULL; //Картинка для рисования в потоке DWORD ThreadID=NULL; //Идентификатор потока рисования int dY=1; //Шаг смещения рисунка, может быть и — и +. int TimeScroll=10; //Таймаут для потока в миллисекундах bool idScroll=false; //Флажок для завершения потока //---------- Поток рисования ------------ DWORD CALLBACK ThreadDraw(void *P) { if(!ImageBmp){ThreadID=NULL; return 0;} //Временный битмап Graphics::TBitmap *tmpBmp=new Graphics::TBitmap(); tmpBmp->Width=Form1->PaintBox1->Width; tmpBmp->Height=Form1->PaintBox1->Height; //Создадим HDC для собственного пользования HDC hdcTo=CreateCompatibleDC(tmpBmp->Canvas->Handle); //Привяжем битмап к HDC SelectObject(hdcTo,tmpBmp->Handle); //Создадим HDC для собственного пользования HDC hdcFrom=CreateCompatibleDC(ImageBmp->Canvas->Handle); //Привяжем битмап к HDC SelectObject(hdcFrom,ImageBmp->Handle); //Используемые переменные, не все они вообще-то необходимы int X0,W0,Y0,H0,X1,W1,Y1,H1; int hTo=tmpBmp->Height; int hFrom=ImageBmp->Height; X0=0; W0=tmpBmp->Width; Y0=0; H0=hTo; X1=0; W1=ImageBmp->Width; Y1=0; //Приступаем к беганию по кругу while(idScroll) { //Поспим, чтобы дать другим поработать Sleep(TimeScroll); //В области вывода может оказаться несколько кадров //рисуемой картинки //Рисуем верхний кусок if(Y1 < 0)Y1 =Y1%hFrom+hFrom; if(Y1 > hFrom)Y1 =Y1%hFrom; H0=hFrom-Y1; if(H0 >hTo)H0=hTo; if(H0) StretchBlt(hdcTo,X0,Y0,W0,H0,hdcFrom,X1,Y1,W1,H0,SRCCOPY); //До рисовываем цепочку до конца while(hTo >H0) { H1=hTo-H0; if(H1 >hFrom)H1=hFrom; if(H1) StretchBlt(hdcTo,X0,Y0+H0,W0,H1,hdcFrom,X1,0,W1,H1,SRCCOPY); H0+=H1; } //У PaintBox' | |
Devnvd (27-12-2002 20:29:27) http://devnvd.narod.ru | |
Извините,но весь текст не влез. Если кому необходим исходник этого примера пишите письмо по адресу devnvd@imail.ru | |
Devnvd (28-12-2002 09:03:59) | |
Теперь наверно точно полностью. Приведу полностью пример рисования на форме в потоке Тонкое место здесь это использование Sleep. Вместо него лучше сделать вертушку PeekMessage<->DispatchMessage или организовывать Event'ы. Чтобы быстро рисовать надо рисовать в Bitmap и после формирования изображения копировать его на экран. #include #pragma hdrstop #include "Unit1.h" //--------------------------------------------------------------------------- #pragma package(smart_init) #pragma resource "*.dfm" TForm1 *Form1; /***************************************************************** Прорисовка в режиме SCROLL картинки ImageBmp в области PaintBox1'а PaintBox вообще-то здесь не к месту, в потоке лучше рисовать на TWinControl. Здесь PaintBox используем только в качестве примера Используются компоненты: TForm *Form1; // Сама форма на которой всё происходит TButton *Button1; //Вызов диалога зарузки картинки TPaintBox *PaintBox1; // Задаёт область рисования на форме TOpenDialog *OpenDialog1; // Для ввода имени файла BMP-картинки TEdit *Edit1; // Для изменения величины таймаута TLabel *Label1; //Для отображения текущих координат мыши в PaintBox'е ******************************************************************/ Graphics::TBitmap *ImageBmp=NULL; //Картинка для рисования в потоке DWORD ThreadID=NULL; //Идентификатор потока рисования int dY=1; //Шаг смещения рисунка, может быть и — и +. int TimeScroll=10; //Таймаут для потока в миллисекундах bool idScroll=false; //Флажок для завершения потока //---------- Поток рисования ------------ DWORD CALLBACK ThreadDraw(void *P) { if(!ImageBmp){ThreadID=NULL; ExitThread(0); return 0;} //Временный битмап Graphics::TBitmap *tmpBmp=new Graphics::TBitmap(); tmpBmp->Width=Form1->PaintBox1->Width; tmpBmp->Height=Form1->PaintBox1->Height; //Создадим HDC для собственного пользования HDC hdcTo=CreateCompatibleDC(tmpBmp->Canvas->Handle); //Привяжем битмап к HDC SelectObject(hdcTo,tmpBmp->Handle); //Создадим HDC для собственного пользования HDC hdcFrom=CreateCompatibleDC(ImageBmp->Canvas->Handle); //Привяжем битмап к HDC SelectObject(hdcFrom,ImageBmp->Handle); //Используемые переменные, не все они вообще-то необходимы int X0,W0,Y0,H0,X1,W1,Y1,H1; int hTo=tmpBmp->Height; int hFrom=ImageBmp->Height; X0=0; W0=tmpBmp->Width; Y0=0; H0=hTo; X1=0; W1=ImageBmp->Width; Y1=0; //Приступаем к беганию по кругу while(idScroll) { //Поспим, чтобы дать другим поработать Sleep(TimeScroll); //В области вывода может оказаться несколько кадров //рисуемой картинки //Рисуем верхний кусок if(Y1 < 0)Y1 =Y1%hFrom+hFrom; if(Y1> hFrom)Y1 =Y1%hFrom; H0=hFrom-Y1; if(H0>hTo)H0=hTo; if(H0) StretchBlt(hdcTo,X0,Y0,W0,H0,hdcFrom,X1,Y1,W1,H0,SRCCOPY); //До рисовываем цепочку до конца while(hTo>H0) { H1=hTo-H0; if(H1>hFrom)H1=hFrom; if(H1) StretchBlt(hdcTo,X0,Y0+H0,W0,H1,hdcFrom,X1,0,W1,H1,SRCCOPY); H0+=H1; } //У PaintBox'а нет Handle и постоянного HDC //PaintBox это всего лишь обозначение места, там где его положили //Поэтому берём HDC окна на котором он лежит //Так как окно Form1 может подвергаться процедуре ReCreate, // то будем получать его контекст динамически HDC hdc=GetDC(Form1->Handle); //Получим место на окне где лежит PaintBox, вдруг его сместили :) int XP=Form1->PaintBox1->Left; int YP=Form1->PaintBox1->Top; BitBlt(hdc,XP,YP,W0,hTo,hdcTo,0,0,SRCCOPY); ReleaseDC(Form1->Handle,hdc); Y1+=dY; //Сдвинемся } //Убираем то что мы для себя временно сделали DeleteDC(hdcTo); DeleteDC(hdcFrom); delete tmpBmp; //Завершаем поток ThreadID=NULL; return 0; } //--------------------------------------------------------------------------- __fastcall TForm1::TForm1(TComponent* Owner) : TForm(Owner) { //Чтобы не моргало, мы сами будем перерисовывать фон Form1->ControlStyle< //--------------------------------------------------------------------------- //Считываем файл картинки заносим её в ImageBmp //Запускаем поток для отображения картинки void __fastcall TForm1::Button1Click(TObject *Sender) { if(!OpenDialog1->Execute())return; //Завершим поток на всякий случай idScroll=false; while(ThreadID){Application->ProcessMessages();} if(!ImageBmp)ImageBmp=new Graphics::TBitmap(); try{ ImageBmp->LoadFromFile(OpenDialog1->FileName); }catch(...){ShowMessage("Error load Image");return;} idScroll=true; HANDLE hThread=::CreateThread(0,0,ThreadDraw,0,0,&ThreadID); ::SetThreadPriority(hThread,THREAD_PRIORITY_NORMAL); ::CloseHandle(hThread); //Больше не нужен } //--------------------------------------------------------------------------- //Если нажмём левую кнопку мыши в области PainBox'а // то сможем смещать всю форму // Если дополнительно нажмём клавишу Cntr, // то сместим только PaintBox void __fastcall TForm1::PaintBox1MouseMove(TObject *Sender, TShiftState Shift, int X, int Y) { static int X0,Y0,id_set=0; if(!Shift.Contains(ssLeft)) { id_set=0; Label1->Caption="X="+IntToStr(X)+"rnY="+IntToStr(Y); return; } if(!id_set && Shift.Contains(ssLeft)) { X0=X; Y0=Y; id_set=1; return; }//Запомним место где прижали мышь TControl *mb=(TControl *)Sender; if(!Shift.Contains(ssCtrl)&& mb->Parent) { //сместим родителя mb->Parent->Left+=(X-X0); mb->Parent->Top+=(Y-Y0); } else { //сместим компонент mb->Left+=(X-X0); mb->Top+=(Y-Y0); } } //--------------------------------------------------------------------------- //Рисуем фон окна Form1 void __fastcall TForm1::FormPaint(TObject *Sender) { Canvas->Brush->Color=Color; Canvas->FillRect(ClientRect); } //--------------------------------------------------------------------------- //Подтираем за собой void __fastcall TForm1::FormDestroy(TObject *Sender) { idScroll=false; while(ThreadID){Application->ProcessMessages();} if(ImageBmp)delete ImageBmp; } //--------------------------------------------------------------------------- //Динамически меняем величину таймаута для потока void __fastcall TForm1::Edit1Change(TObject *Sender) { int i=atoi(Edit1->Text.c_str()); if(i <1000)TimeScroll=i; } |