Экспертная система Delphi.int.ru

Сообщество программистов
Общение, помощь, обмен опытом

Логин:
Пароль:
Регистрация | Забыли пароль?

Delphi.int.ru Expert

Другие разделы портала

Переход к вопросу:

#   

Статистика за сегодня:  


Лучшие эксперты

Подробнее »



Вопрос # 4 557

/ вопрос открыт /

Здравствуйте уважаемые эксперты!
Мой вопрос касается создания потоков внутри компонента.
Суть проблемы такова
я создал простенький компонент на основе TPanel, для работы с графикой. При обсчете графики вся программа жутко тормозит, ввиду чего было принято решение создавать новый поток для расчетов, который будет уничтожаться после рассчета всех параметров.

Проблема такова что компилятор ругается на свойства TMyPanel типа Width внутри функции потока и пишет что они не определены, в то же время если закомментировать все строки связанные с созданием потока, то все нормально. Вопрос в том как можно решить эту проблему?

Приложение:
  1. unit MyPanel;
  2.  
  3. interface
  4.  
  5. uses
  6. SysUtils, Classes, Controls, ExtCtrls, Graphics;
  7.  
  8. type
  9. TMyPanel = class(TPanel)
  10. private
  11. { Private declarations }
  12.  
  13. protected
  14. property Canvas;
  15. procedure Paint();Override;
  16. { Protected declarations }
  17.  
  18. public
  19. Constructor Create(AOwner: TComponent); Override;
  20. Property ClientRect;
  21. Property BoundsRect;
  22. { Public declarations }
  23.  
  24. published
  25. property width default 49;
  26. property Height default 41;
  27. { Published declarations}
  28. end;
  29.  
  30.  
  31. procedure Register;
  32.  
  33. function CreateThread(lps:pointer; cbStack:Cardinal; lpStartAddr:pointer;
  34.  
  35. lpvThreadParam:pointer; fdwCreate:Cardinal; lpIDThread:Cardinal ):THandle;
  36. STDCall; external 'Kernel32' name 'CreateThread';
  37.  
  38. function GetExitCodeThread(hThread:THandle; lpExitCode:Cardinal):boolean;
  39. STDCall; external 'Kernel32' name 'GetExitCodeThread';
  40.  
  41. procedure ExitThread(dwExitCode:Cardinal);
  42. STDCall; external 'Kernel32' name 'ExitThread';
  43.  
  44. Function MyThread(p:pointer):LongInt; STDCall;
  45.  
  46. var
  47. ThreadHandl: THandle;
  48. ThreadID: Cardinal;
  49.  
  50.  
  51. implementation
  52.  
  53. Constructor TMyPanel.Create(AOwner: TComponent);
  54. Begin
  55. Inherited Create(AOwner);
  56.  
  57. Width:=49;
  58. Height:=41;
  59. Caption:='';
  60.  
  61. End;
  62.  
  63. procedure Register;
  64. begin
  65. RegisterComponents('Standard', [TMyPanel]);
  66. end;
  67.  
  68. Procedure TMyPanel.Paint;
  69. Begin
  70.  
  71. ThreadHandl:=CreateThread(nil,0,@MyThread,nil,0,ThreadID);
  72.  
  73. End;
  74.  
  75.  
  76. Function MyThread(p:pointer):LongInt; STDCall;
  77. var
  78. ExitCode: Integer;
  79.  
  80. i1,i2: Integer;
  81. Begin
  82. GetExitCodeThread(ThreadHandl,ExitCode);
  83.  
  84. i1:=Width;
  85. i2:=Height;
  86.  
  87.  
  88. ExitThread(ExitCode);
  89. End;
  90.  
  91.  
  92. end.


SOA Вопрос ожидает решения (принимаются ответы, доступен мини-форум)

Вопрос задал: SOA (статус: Посетитель)
Вопрос отправлен: 3 сентября 2010, 13:29
Состояние вопроса: открыт, ответов: 1.

Ответ #1. Отвечает эксперт: Вадим К

Здравствуйте, SOA!
сколько хлама в таком маленьком коде!
собственно с проблемы.
MyThread - это функция, а не метод класса TMyPanel, поэтому доступа до его полей и методов не может иметь. Что бы обойти это - нужно передать указатель на объект.
например так

ThreadHandl:=CreateThread(nil,0,@MyThread,self,0,ThreadID);
(может там приведение типов для self понадобиться)
а в самой процедуре делаем так
Function MyThread(p:pointer):LongInt; STDCall;
var
ExitCode: Integer;
 
i1,i2: Integer;
panel:TMyPanel;
Begin
panel := TMypanel(p);
GetExitCodeThread(ThreadHandl,ExitCode);
 
i1:=panel.Width;
i2:=panel.Height;
 
 
ExitThread(ExitCode);
End;

Вторая ошибка - это создание треда в методе Paint. В этом методе нужно делать строго одно - отрисовать картинку. Вы же создаете тред. Картинка отрисована не будет. Тред сам то может рисовать, но это будет очень неефективно - будут мерцания.

Метод Paint вызывается системой, когда она хочет отрисовать компонент. Поэтому если в нем быстро рисовать все что нужно, то будет очень красиво работать.

Как же сделать более правильно?
1) воспользоваться классом TThread, а не изобретать велосипед с системными функциями. Они как минимум ничего не знают о VCL и в будущем это может быть выражено в виде больших глюков.
2) тред запускать в момент создания компонента.
3) тред делает рассчеты и рисует на TBitmap'е. А вот метод Paint просто берет этот битмап и просто копирует на панель. Конечно, тред может выставить специальный флажок, что данные ещё не готовы или использовать два битмапа (на одном тред рисует, на второй переносит, когда изображение достигает какого то логического этапа и с этого битмапа уже рисует Paint).
4) Не забывать о том, что есть такое понятие как критические секции и мютексы и аккуратно синхронизировать работу.
5) подумать о том, что Panel - не очень хороший компонент в данном случае и наследоваться от TGraphicsControl или чего то подобного.

Ответ отправил: Вадим К (статус: Академик)
Время отправки: 3 сентября 2010, 14:16
Оценка за ответ: 5

Комментарий к оценке: Спасибо за подсказку про TThread.

Мини-форум вопроса

Всего сообщений: 20; последнее сообщение — 7 сентября 2010, 15:50; участников в обсуждении: 2.
SOA

SOA (статус: Посетитель), 3 сентября 2010, 16:56 [#1]:

Нет так не получается, возникает ошибка обращения к памяти при первой же попытке создать поток. Пытался передавать параметры через глобальные переменные, в этом случае приложение пишет что у той или иной переменной неизвестный тип, хотя минуту назад до изменений тип был известен :)
Также пытался передавать параметры в указателях, но там программа ведет себя примерно также, только пишет не про тип а о том что некоторые параметры должны быть переменными.
В общем упирается как только может.

Попробую через TThread.
Вадим К

Вадим К (статус: Академик), 3 сентября 2010, 18:33 [#2]:

ну может покажете результирующий код? например там, где через параметры передаете.
А вот с глобальными параметрами - это можно сразу выкидывать.
Галочка "подтверждения прочтения" - вселенское зло.
SOA

SOA (статус: Посетитель), 4 сентября 2010, 07:51 [#3]:

Извините то код я уже удалил, там все сводилось к массиву указателей объявленному в глобальных параметрах, после чего перед вызовом функции создания потока в этот массив записывались адреса параметров из TMyPanel.
Собственно проблему удалось решить через TThread, привожу код

unit MyPanel;
 
interface
 
uses
  SysUtils, Classes, Controls, ExtCtrls, Graphics, Types;
 
type
  TMyPanel = class(TPanel)
  private
  FSrcCanvas: TCanvas;
    { Private declarations }
 
  protected
  property Canvas;
  procedure Paint();Override;
    { Protected declarations }
 
  public
  property SrcCanvas:TCanvas read FSrcCanvas write FSrcCanvas;
  Constructor Create(AOwner: TComponent); Override;
  Property ClientRect;
  Property BoundsRect;
    { Public declarations }
 
  published
  property width default 49;
  property Height default 41;
    { Published declarations}
  end;
 
 
 
type
  TMyThread = class(TThread)
  procedure Execute; override;
  procedure Raschet;
  End;
 
procedure Register;
 
 
var
Thread1: TMyThread;
 
MyWidth, MyHeight: Integer;
MyCanvas, MySrcCanvas: TCanvas;
MyClientRect, MyBoundsRect: TRect;
 
 
implementation
 
 
 
Constructor TMyPanel.Create(AOwner: TComponent);
Begin
Inherited Create(AOwner);
 
Width:=49;
Height:=41;
Caption:='';
 
End;
 
 
 
Procedure TMyThread.Execute();
Begin
Synchronize(Raschet);
 
End;
 
 
 
Procedure TMyThread.Raschet();
var
i1,i2: Integer;
Begin
 
i1:=MyWidth;
i2:=MyHeight;
 
Thread1.Terminate;
End;
 
 
 
procedure Register;
begin
  RegisterComponents('Standard', [TMyPanel]);
end;
 
 
 
Procedure TMyPanel.Paint;
Begin
 
MyWidth:=Width;
MyHeight:=Height;
 
Thread1:=TMyThread.Create(false);
 
 
End;
 
 
 
end.

Там тоже компилятор вначале ругался на некоторые параметры, но после включения в uses Types.pas все заработало, правда новый поток не помог повысить скорость исполнения кода, ЖУТКИЕ тормоза при отрисовке как были так и есть :)
Вадим К

Вадим К (статус: Академик), 4 сентября 2010, 18:00 [#4]:

Тормоза потому, что рисуете не там, где нужно. Но самая большая ошибка - это строка
Synchronize(Raschet);
дело в том, что такая конструкция выполняется в главном потоке. То есть вся выгода от треда в данном случае нивелируется.
Во вторых, я уже писал, что тред нужно создавать в Create TMyPanel.
И на последок. Если в коде треда Вы обращаетесь только к элементам канвы, то можно не синхронизировать дополнительно - они уже синхронизированы и тредобезопасны.
Галочка "подтверждения прочтения" - вселенское зло.
SOA

SOA (статус: Посетитель), 5 сентября 2010, 14:04 [#5]:

Тормоза будут даже если рисовать в самой процедуре Paint, потому что цикл слишком длинный, собственно поэтому и думал что новый поток поможет решить проблему.
Вадим К

Вадим К (статус: Академик), 5 сентября 2010, 19:21 [#6]:

новый поток решит то проблему. Но нужно правильно решать. Я уже писал выше, как ее нужно решать и как ее решают. Но Вы упорно ищете странный путь решения. Я не знаю, почему.
Галочка "подтверждения прочтения" - вселенское зло.
SOA

SOA (статус: Посетитель), 6 сентября 2010, 09:57 [#7]:

Если создавать поток при создании компонента, то я не вижу возможности передать параметры компонента (width,height) внутрь потока т.к. в том коде что я написал выше эти параметры передаются через глобальные переменные.
При создании потока в событии Create компонента пытался инициализировать эти параметры через систему задержек

поток исполняется каждые 150 мс, Paint перерисовывает Canvas каждые 300 мс по условию

MyWidth:=Width;
MyHeight:=Height;
Sleep(300);

While true do
if ready=true then
Begin
Canvas.CopyRect(Rect(0,0,?,?),MyBitmap.Canvas,Rect(0,0,?,?));
break;
End;

при таком подходе панель даже не перерисовывалась.
Вадим К

Вадим К (статус: Академик), 6 сентября 2010, 10:28 [#8]:

с глобальными переменными в коде компонента Вы проживете не долго. Если использовать TThread, то никто не мешает завести у его наследника поле типа TMyPanel и при создании инициализировать ссылкой на него.
Единственное, что тред придется создавать спящим, а потом, когда все инициализируется, будить.
А код приведенный выше - это в коде треда я так понимаю? Но тогда не понятен смысл фразы

Цитата (SOA):

Paint перерисовывает Canvas каждые 300 мс по условию

Ваш код отработает ровно один раз (там ведь есть break).

Странно выглядит код - зачем там задержка в 300 мс я не понимаю. Смысла особого никакого.

Приводите полный код, сложно разбираться в таких отрывках. и если Вы не хотите приводить расчетную часть, просто вставляйте коментарий вида {тут считаем} или что то в этом роде.
Галочка "подтверждения прочтения" - вселенское зло.
SOA

SOA (статус: Посетитель), 6 сентября 2010, 10:36 [#9]:

Нет приведенный код касется функции Paint, Расчеты производятся в Thread где выставляется задержка sleep(150) после каждого цикла расчетов

MyThread.Rascet()
Begin
Ready:=false;
{расчеты}
Ready:=True;
Sleep(150);
End;

TMyPanel.Paint();
Begin

MyWidth:=Width;
MyHeight:=Height;
try
Thread1.Resume;
Except
End;
Sleep(300); // Чтоб поток успел проинициализировать переменные

While true do // Ждем результат
if ready=true then
Begin
Canvas.CopyRect(Rect(0,0,?,?),MyBitmap.Canvas,Rect(0,0,?,?));
break;
End;
End;

P.S. про вставку Thread в TMyPanel как то не подумал спасибо, попробую.
Вадим К

Вадим К (статус: Академик), 6 сентября 2010, 10:45 [#10]:

Sleep(300); - это первая ошибка.
While true do // Ждем результат - а это вторая.
Код Paint'а должен отрабатываться максимально быстро. МАКСИМАЛЬНО. поэтому всякие задержки - это ненужности.
Можно код отрисовки сделать минимальным - например таким
if ready then 
Canvas.CopyRect(Rect(0,0,?,?),MyBitmap.Canvas,Rect(0,0,?,?))
else
Canvas.CopyRect(Rect(0,0,?,?),MyEmpty.Canvas,Rect(0,0,?,?));
здесь в коде есть ещё MyEmpty - это спициальная канва, на которую мы загружаем "пустой рисунок".
При таком подходе картинка на канве появиться как только так сразу:)

Кстати, в этом коде могут быть проблемы из за переменной Ready. Дело в том, что компилятор может оптимизировать ее и ее значение в разных тредах будет не синхронно.
Галочка "подтверждения прочтения" - вселенское зло.
SOA

SOA (статус: Посетитель), 6 сентября 2010, 12:41 [#11]:

>Если использовать TThread, то никто не мешает завести у его наследника поле типа TMyPanel и при создании инициализировать ссылкой на него.

Попытался сделать так, вот что получилось

type TMyPanel = class(TPanel); // Если убрать здесь, пишет в TMyThread о неизвестном типе 
 
type
  TMyThread = class(TThread)
  procedure Execute; override;
  procedure Raschet;
 
  private
  FPanel: TMyPanel;
  {Private declarations}
  public
  property Panel: TMyPanel read FPanel write FPanel;
  {Public declarations}
  End;
 
 
 
type
  TMyPanel = class(TPanel) // Пишет об двойном объявлении типа
  private
  FThread1: TMyThread;
  FMyBitmap: TBitmap;
.
.
.

В книге нашел метод упреждающего объявления классов, но с типами похоже это не работает :(
Вадим К

Вадим К (статус: Академик), 6 сентября 2010, 12:54 [#12]:

Цитата (SOA):

метод упреждающего объявления классов, но с типами похоже это не работает

прочитайте внимательно текст. классы это тоже типы.
А не работает потому, что сделали не упреждающее объявление, а просто объявление.
Нужно писать так
TMyPanel = class;
Галочка "подтверждения прочтения" - вселенское зло.
SOA

SOA (статус: Посетитель), 6 сентября 2010, 13:28 [#13]:

Вот так

unit MyPanel;
 
interface
 
uses
  SysUtils, Classes, Controls, ExtCtrls, Graphics;
 
TMyPanel=class;  // Ругается Declaration expected but identifer 'TMyPanel' found
 
type
  TMyThread = class(TThread)
  procedure Execute; override;
  procedure Raschet;
 
  private
  FPanel: TMyPanel;
  {Private declarations}
  public
  property Panel: TMyPanel read FPanel write FPanel;
  {Public declarations}
  End;
 
type
  TMyPanel = class(TPanel)
 
  private
  FThread1: TMyThread;
  FMyBitmap: TBitmap;
    { Private declarations }

Тоже ругается на то что ожидал увидеть код, видит идентификатор.
Вадим К

Вадим К (статус: Академик), 6 сентября 2010, 15:22 [#14]:

ох - ох. ну он же пишет - хочу декларацию. а они с type!
то есть правильно это должно выглядеть так
unit MyPanel;
 
interface
 
uses
  SysUtils, Classes, Controls, ExtCtrls, Graphics;
type 
TMyPanel=class;  
 
//type здесь можно этот type убрать
  TMyThread = class(TThread)
  procedure Execute; override;
  procedure Raschet;
 
  private
  FPanel: TMyPanel;
  {Private declarations}
  public
  property Panel: TMyPanel read FPanel write FPanel;
  {Public declarations}
  End;
 
// type - и здесь.
  TMyPanel = class(TPanel)
 
  private
  FThread1: TMyThread;
  FMyBitmap: TBitmap;
    { Private declarations }
Галочка "подтверждения прочтения" - вселенское зло.
SOA

SOA (статус: Посетитель), 6 сентября 2010, 16:19 [#15]:

Действительно так не ругается и свойства можно импортировать.
:)
SOA

SOA (статус: Посетитель), 6 сентября 2010, 18:33 [#16]:

Да свойства импортировать можно, но из за этого образуется петля в процедуре TMyPanel.Create, которая съедает память, до тех пор пока система не прихлопнет ее.

unit MyPanel;
 
interface
 
uses
  SysUtils, Classes, Controls, ExtCtrls, Graphics;
 
type
  TMyPanel=class;
 
//type
  TMyThread = class(TThread)      // MyThread
  procedure Execute; override;
  procedure Raschet;
 
  private
  FPanel: TMyPanel;
  {Private declarations}
 
  public
  property Panel: TMyPanel read FPanel write FPanel;
  {Public declarations}
  End;
 
//type
  TMyPanel = class(TPanel)  // MyPanel
 
  private
  FThread1: TMyThread;
  FMyBitmap: TBitmap;
    { Private declarations }
 
  protected
  property Canvas;
  procedure Paint();Override;
    { Protected declarations }
 
  public
  Constructor Create(AOwner: TComponent); Override;
    { Public declarations }
 
  published
  property width default 49;
  property Height default 41;
  property Thread1: TMyThread read FThread1 write FThread1;
  property MyBitmap: TBitmap read FMyBitmap write FMyBitmap;
    { Published declarations}
  end;
 
 
procedure Register;
 
implementation
 
Constructor TMyPanel.Create(AOwner: TComponent);
Begin
Inherited Create(AOwner);     //1
 
Thread1:=TMyThread.Create(true);   //2       
Thread1.Panel.MyBitmap:=TBitmap.Create; //3 Тут почемуто выдает ошибку обращения к памяти
Thread1.Panel:=TMyPanel.Create(nil);  //4 здесь образуется петля и указатель следующей процедуры прыгает на первый
пункт.
 
 
Width:=49;
Height:=41;
Caption:='';
 
End;
 
 
 
Procedure TMyThread.Execute();
Begin
Synchronize(Raschet);
 
End;
 
 
Procedure TMyThread.Raschet();
Begin
{расчеты}
End;
 
 
 
procedure Register;
begin
  RegisterComponents('Standard', [TMyPanel]);
end;
 
 
 
Procedure TMyPanel.Paint;
Begin
 
if Thread1.Suspended=true then // Запускаем поток
Thread1.Resume;
 
Thread1.Panel.MyBitmap.Width:=Width;
Thread1.Panel.MyBitmap.Height:=Height;
 
Thread1.Panel.Top:=BoundsRect.Top;
Thread1.Panel.Left:=BoundsRect.Left;
{Thread1.Panel.ClientRect:=ClientRect;}
Thread1.Panel.BoundsRect:=BoundsRect;
Thread1.Panel.SrcCanvas:=SrcCanvas;
 
Canvas.CopyRect(ClientRect,Thread1.Panel.MyBitmap.Canvas,Rect(0,0,Width,Height));
 
End;
 
 
 
 
End.
Вадим К

Вадим К (статус: Академик), 6 сентября 2010, 21:04 [#17]:

Constructor TMyPanel.Create(AOwner: TComponent);
Begin
Inherited Create(AOwner); //1

Thread1:=TMyThread.Create(true); //2
Thread1.Panel.MyBitmap:=TBitmap.Create; //3 Тут почемуто выдает ошибку обращения к памяти
Thread1.Panel:=TMyPanel.Create(nil); //4 здесь образуется петля и указатель следующей процедуры прыгает на первый
пункт.

В точке 3 ссылки на панель ещё нет. Компилятор не может "заглянуть вперед" и посмотреть, что там инициализировали. К тому же, зачем так, через двойной указатель ссылаться на самого же себя? не проще просто написать в этом месте
MyBitmap:=TBitmap.Create;
теперь к точке 4. А тут вообще бардак. Этой строкой Вы создаете ещё одну панель. Эта панель ничего не знает о панели родителе (хотя учитывая иерархию - это уже панель бабушка:) ).
Правильно строка должна выглядеть где то так
Thread1.Panel := self;

Теперь пройдемся по процедуре Paint.
Procedure TMyPanel.Paint;
Begin
 
//if Thread1.Suspended=true then // Запускаем поток - здесь писать "= true" не нужно
// вот так будет куда красивее
if Thread1.Suspended then
Thread1.Resume;
 // эти две строки ниже - плохой код. Ведь в этот момент у Вас уже работает тред. 
 //И в общем нужно исходить из того, что теперь работает параллельно два куска кода. ПАРАЛЛЕЛЬНО! 
Thread1.Panel.MyBitmap.Width:=Width;
Thread1.Panel.MyBitmap.Height:=Height;
 //А вот эти строки ниже для меня вообще загадка. Что они делают...
 //то, что они меняют размеры панели и других компонент - я понимаю. Но зачем??? и в процедуре отрисовки...
//не смотря на все вышеуказанное, эту строку можно упросить.
Thread1.Panel.Top:=BoundsRect.Top; // до такого Top:=BoundsRect.Top;
 
Thread1.Panel.Left:=BoundsRect.Left;
{Thread1.Panel.ClientRect:=ClientRect;}
Thread1.Panel.BoundsRect:=BoundsRect;
Thread1.Panel.SrcCanvas:=SrcCanvas;
 
Canvas.CopyRect(ClientRect,Thread1.Panel.MyBitmap.Canvas,Rect(0,0,Width,Height));
 
End;
Галочка "подтверждения прочтения" - вселенское зло.
SOA

SOA (статус: Посетитель), 7 сентября 2010, 14:29 [#18]:

Действительно так рисует, но тормоза при отрисовке, всеравно присутствуют :(
Также для отрисовки пришлось добавить Panel.Redraw в Paint.

В целом код практически такой как вы и написали, с учетом комментариев конечно

Constructor TMyPanel.Create(AOwner: TComponent);
Begin
Inherited Create(AOwner);
 
Thread1:=TMyThread.Create(true);
Thread1.Panel:=Self;
MyBitmap:=TBitmap.Create;
 
End;
 
 
 
Procedure TMyPanel.Paint;
var
i1,i2: Integer;
c: TColor;
r,g,b:byte;
Begin
 
if Thread1.Suspended then
Thread1.Resume;
 
MyBitmap.Width:=Width;
MyBitmap.Height:=Height;
 
Canvas.CopyRect(ClientRect,MyBitmap.Canvas,Rect(0,0,Width,Height));
 
Thread1.Panel.Refresh;
end;

Попробую поискать другой путь решения, но в целом всеравно довольно неплохой опыт для меня.
Вадим К

Вадим К (статус: Академик), 7 сентября 2010, 14:45 [#19]:

Замечания к коду.
Первое - если что то рассчитывается двадцать минут, то тред не поможет это ускорить. Просто разгрузит интерфейс. Но если машина двухядерная, и сделать два треда, правильно распараллелить, то можно получить ускорение до двух раз.

MyBitmap.Width:=Width;
MyBitmap.Height:=Height;
если тред орудует объектом MyBitmap (рисует на нем), то эти строки тут только навредят.

а вот в этой строке
Thread1.Panel.Refresh;
два чуда.
1) если ее сократить до Refresh - то ничего не измениться.
2) вызывать ее внутри процедуры отрисовки - плохо - она фактически вызвет ещё раз процедуру отрисовки.

пусть лучше тред, когда закончит рисовать, вызовет refresh. Вот тогда Paint просто скопирует картинку и все будет готово.
вот мое краткое видение кода
Constructor TMyPanel.Create(AOwner: TComponent);
Begin
Inherited Create(AOwner);
 
Thread1:=TMyThread.Create(true);
Thread1.Panel:=Self;
MyBitmap:=TBitmap.Create;
 
MyBitmap.Width:=Width;
MyBitmap.Height:=Height;
 
Thread1.Resume;
End;
 
 
 
Procedure TMyPanel.Paint;
Begin
if not Thread1.ready then 
Canvas.CopyRect(ClientRect,MyEmptyBitmap.Canvas,Rect(0,0,Width,Height))
else
Canvas.CopyRect(ClientRect,MyBitmap.Canvas,Rect(0,0,Width,Height));
end;
MyEmptyBitmap - это специальное поле TMyPanel, которое хранит "пустую картинку" - например картинку с текстом "идет рассчет".

код треда где то такой
procedure TThread.Execute();
begin
  inherited;
  {какие то рассчеты};
  ready := true;// ready - свойство класса треда, типа boolean
  // добавить самостоятельно:) и в конструкторе выставить равным false.
  panel.refresh(); // или repaint
end;
Галочка "подтверждения прочтения" - вселенское зло.
SOA

SOA (статус: Посетитель), 7 сентября 2010, 15:50 [#20]:

> если что то рассчитывается двадцать минут, то тред не поможет это ускорить. Просто разгрузит интерфейс.

Не спорю.

Чтобы оставлять сообщения в мини-форумах, Вы должны авторизироваться на сайте.

Версия движка: 2.6+ (26.01.2011)
Текущее время: 22 февраля 2025, 11:54
Выполнено за 0.05 сек.