Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > Delphi: Общие вопросы > Передвижение формы по экрану


Автор: bagos 30.11.2010, 20:59
Уважаемые поделитесь идеями, как сделать максимально сглаженным эффект передвижения формы по экрану.
то что сделал не устраивает
Код

procedure TForm1.Button1Click(Sender: TObject);
var
  i:integer;
begin
if form1.Top < 0 then
begin
 for i:=0 to Height*6 - 1 do
  begin
    if Top <> 100*3 then
      Top := Top + 1;
      //Sleep(1);
  end;
end else
begin
for i:=0 to (Height*6 - 1) div 5 do
begin
  if Top <> - Height then
  form1.Top := Form1.Top-5;
  Sleep(1);
end;
end;
          end;


Автор: Xenag 30.11.2010, 23:09
Добавь в оба цикла перед Sleep
Application.ProcessMessages;

Автор: remax 1.12.2010, 00:38
Sleep надо совсем убрать. Эта безусловная задержка не учитывает, что параллельно с Вашей программой выполняются и другие процессы. Для того, чтобы организовать "нормальную" равномерную задержку - надо использовать функцию GetTickCount. 
Да, и конечно использовать Application.ProcessMessages; - иначе не будет нормально отрисовываться форма...


===========
function GetTickCount: Longint;

Считывает вpемя, пpошедшее с момента запуска системы.
Возвpащаемое значение - Пpошедшее вpемя (в миллисекундах). 
 

Автор: bagos 1.12.2010, 01:57
Спасибо за ответы!
Код

var
  i:integer;
  formtop : integer;
begin
    GetTickCount;
    Application.ProcessMessages;
    formtop := form1.Top;
    for i:=0 to formtop do
      if form1.Top > 0 then
        form1.Top := form1.Top - 5;


также пихал процесмесаг в цикл и геттиккаунт не помогает, шлейф солидный

Автор: remax 1.12.2010, 10:08
не-не, GetTickCount нужен не сам по себе, а нужен результат который он возвращает. Именно его использовать при определении задержки между отображением.

Кроме того, скорость отрисовки формы СУЩЕСТВЕННО зависит от ее содержимого.

И, самое главное, если недостаточные видеоресурсы или неправильно настроена система, то хоть что делай а толку не будет. Для начала надо убедиться, что, например, окно Блокнота перемещается (мышкой) без шлейфа...


Вариант с таймером (интервал = 20)
Код

unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, ExtCtrls;

type
  TForm1 = class(TForm)
    Timer1: TTimer;
    procedure Timer1Timer(Sender: TObject);
  private
    n:integer;
  public
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.Timer1Timer(Sender: TObject);
begin
  timer1.Enabled:=false;
  n:=(n+3) mod 360;
  top:=150+round(75*sin(n/57.3));
  left:=150+round(75*cos(n/57.3));
  Application.ProcessMessages;
  timer1.Enabled:=true;
end;

end.


Вариант с синхронизацией по GetTickCount (интервал перерисовки - те же 20 мс)

Код

unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, ExtCtrls, StdCtrls;

type
  TForm1 = class(TForm)
    Button1: TButton;
    procedure Button1Click(Sender: TObject);
  private
    procedure MyView;
  public
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}


procedure TForm1.Button1Click(Sender: TObject);
begin
  MyView;
end;

// 10 секунд крутим форму по кругу, затем закроем прогу
procedure TForm1.MyView;
var
  n,tm,old:integer;
begin
  tm:=GetTickCount;
  old:=tm;
  repeat
   if (GetTickCount-tm>20) then
    begin
      n:=(n+3*((GetTickCount-tm)div 20)) mod 360;
      top:=150+round(75*sin(n/57.3));
      left:=150+round(75*cos(n/57.3));
      Application.ProcessMessages;
      tm:=GetTickCount;
    end;
  until (tm-old>10000);
  close;
end;

end.



Автор: Snowy 1.12.2010, 12:50
Цитата(remax @  1.12.2010,  00:38 Найти цитируемый пост)
Sleep надо совсем убрать. Эта безусловная задержка не учитывает, что параллельно с Вашей программой выполняются и другие процессы. 
Это с какого такого перепоя?
Как раз в этом и смысл Sleep - замораживать текущий тред на указанное время и передавать уплавление системе.
Если его не ставить, то загрузка процессора будет 100%

Автор: remax 1.12.2010, 14:33
Цитата(Snowy @ 1.12.2010,  12:50)
Цитата(remax @  1.12.2010,  00:38 Найти цитируемый пост)
Sleep надо совсем убрать. Эта безусловная задержка не учитывает, что параллельно с Вашей программой выполняются и другие процессы. 
Это с какого такого перепоя?
Как раз в этом и смысл Sleep - замораживать текущий тред на указанное время и передавать уплавление системе.
Если его не ставить, то загрузка процессора будет 100%


1. Да, Sleep - замораживает текущий тред. Но, его применение в лоб не позволяет обеспечить одинаковые задержки, особенно когда речь идет о малых временнЫх интервалах. А тем более, если система загружена другими ресурсоемкими приложениями, работающими в параллель.

Обратите внимание на параметр Sleep у ТС. Точность 1мс Sleep не обеспечит - обычно речь идет о 10-15 мс.  Т.е., при выборе значения параметра менее 15 задержка через Sleep обеспечит либо 15 мс, либо 0. И это без учета паралельных задач....

2. Для передачи управления системе, обычно, вполне хватает  Application.ProcessMessages; И при разумном его использовании ни о каких 100% загрузки проца речь не идет. (см. примеры из моего предыдущего топика).


Автор: Snowy 1.12.2010, 15:34
Цитата(remax @  1.12.2010,  14:33 Найти цитируемый пост)
Да, Sleep - замораживает текущий тред. Но, его применение в лоб не позволяет обеспечить одинаковые задержки, особенно когда речь идет о малых временнЫх интервалах.
Сколько вешать в граммах? К чему такая точность?
При постоянных условиях, интервал будет примерно одинаков. Для большинства задач этого за глаза.

Цитата(remax @  1.12.2010,  14:33 Найти цитируемый пост)
Для передачи управления системе, обычно, вполне хватает  Application.ProcessMessages; И при разумном его использовании ни о каких 100% загрузки проца речь не идет.
Ок. Речь идёт о 100% загрузке ядра процессора.
В случае 2-х ядерного процессора, загрузка будет 50%...
Ты вхолостую крутишь процессор без перерыва. Конечно там 100% загрузка.

Автор: remax 1.12.2010, 18:00
Цитата(Snowy @ 1.12.2010,  15:34)
Сколько вешать в граммах? К чему такая точность?
При постоянных условиях, интервал будет примерно одинаков. Для большинства задач этого за глаза.

Пишем такой сложнейший код (на форме кнопка и мемо):
Код

procedure TForm1.Button1Click(Sender: TObject);
var n,t1,t2:integer;
begin
  memo1.Lines.Clear;
  for n:=1 to 2000 do
    begin
      t1:=GetTickCount;
      sleep(1);
      t2:=GetTickCount;
      memo1.Lines.Append(inttostr(t2-t1));
    end;
end;


Запускаем. И, о чудо, смотрим результат. Результат представляет собой примерно такую повторяющуюся последовательность:

0
0
0
0
0
0
15
0
0
0
0
0
0
0
16
0
...

Что, конечно, практически не отличается от ожидаемой последовательности
1
1
1
1
1
1
...

Может быть эта проблема точности GetTickCount? По крайней мере "0" - это возможно меньше "1".  Чтож, проверим - заменим  sleep(1) на sleep(5). И что, что мы видим. Наверное ряд 5 или, хотя бы, 4?
Нет, мы опять видим  
16
0
15
0
0
16
0
0
16
0
15
0
16
0
0
Т.е., можно сделать вывод, что маленькая задержка через sleep  либо вообще не делается либо в пределах точности 15 мс. Это  при незагруженном процессоре. Теперь запустим наш тест и чем-то еще загрузим. Я, например, потаскаю по экрану окно другой программы. Смотрим результат:
0
63
0
0
125
62
63
62
47
0
16
0
15
32
31
31
....
На мой взгляд, это, во первых, сильно отличается от поставленной задачи. во вторых, далеко не равномерные интервалы.

Выводы:
1. Использование Sleep не обеспечивает правильных программных задержек. Реализуемая точность  +-15 мс.  (зависит от конкретного компьютера) 
2. При высокой загрузке системы и использовании Sleep возможны многократные увеличения интервалов паузы.
3.  Соответственно, нежелательно использовать Sleep для малых интервалов - величина которых соизмерима с 10-15 мс. Это, если нам нужна точность, то  паузу меньше чем 0.1 с  через Sleep не делаем. 
4. Не используем Sleep и для больших (больших, чем 1 с) интервалов - при этом, внешне прога выглядит как зависшая. т.е. Использовать Sleep можно для неточных пауз в интервале от 0.1 до 1.0 с.
 

Автор: remax 1.12.2010, 18:16
Цитата(Snowy @ 1.12.2010,  15:34)
Цитата(remax @  1.12.2010,  14:33 Найти цитируемый пост)
Для передачи управления системе, обычно, вполне хватает  Application.ProcessMessages; И при разумном его использовании ни о каких 100% загрузки проца речь не идет.
Ок. Речь идёт о 100% загрузке ядра процессора.
В случае 2-х ядерного процессора, загрузка будет 50%...
Ты вхолостую крутишь процессор без перерыва. Конечно там 100% загрузка.

Я так понял, что уважаемый Snowy не счел нужным запустить указанные примеры.
Тогда бы он увидел, что загрузка не все время 100%, а похожа на пилу. Всплески во время движения формы и провалы практически до 0 между движениями. Конечно, абсолютные цифры зависят от параметров каждой конкретной системы. Да и от выбранной дискретности движения. Но, это зависит не от применения/неприменения Sleep, а зависит оттого сколько ресурсов тратится на непосредственно отрисовку движения формы.   

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

П.С. Кстати, у ТС никак не было оговорена необходимость уменьшения загрузки проца. А необходимо было обеспечить плавность перемещения. Даже, если бы была 100% загрузки при равномерном перемещении, то задача была бы выполнена. В отличие от применения sleep...  smile 

Автор: Snowy 2.12.2010, 13:21
Я не собираюсь спорить на тему, что Sleep не даёт высокой точности.
Это очевидно.
Я говорил о том, что для данной задачи, такая точность совершенно ни к чему.

Что касается загрузки.
Да, при вызове ProcessMessages она падает.
Но потом следует несколько сотен циклов полной нагрузки.
То есть мы имеем 100% нагрузку с редкими спадами.
Получается вот такая "пила"
user posted image

А теперь возьмём вариант со Sleep:
Код
var
  n,i:integer;
  w, h: integer;
begin
  w := (Screen.Width - Width) div 2;
  h := (Screen.Height - Height) div 2;
  for i := 1 to 400 do
  begin
    Left := Round((-Cos(pi / 200 * i) + 1) * h);
    Top := Round((Sin(pi / 80 * i) + 1) * h);
    Application.ProcessMessages;
    Sleep(20);
  end;
end;
Получаем:
user posted image

Автор: RinOSpro 2.12.2010, 14:33
remax ты до этого прошивку для атомных часов что ли писал)) Жжешь, смотрю на это "чудо" 

Код

   if (GetTickCount-tm>20) then
    begin
      n:=(n+3*((GetTickCount-tm)div 20)) mod 360;

      tm:=GetTickCount;
    end;
  until (tm-old>10000);


и хочется спросить где ты такую траву берешь)?

Цитата(remax @  1.12.2010,  14:33 Найти цитируемый пост)
Точность 1мс Sleep не обеспечит - обычно речь идет о 10-15 мс

Твой цикл с GetTickCount тоже не обеспечит. Ты слышал что ни будь про вытесняющую многозадачность, и планирование потоков в виндовс? Почитай) Ссылку кидать не буду ибо она прикреплена на очень видном месте)

Ах да, прочти еще раз название темы. Автор и в правду не атомные часы делает у него цель менее амбициозная.

Автор: remax 2.12.2010, 18:11
Цитата(RinOSpro @ 2.12.2010,  14:33)
remax ты до этого прошивку для атомных часов что ли писал)) Жжешь, смотрю на это "чудо" 

Код

   if (GetTickCount-tm>20) then
    begin
      n:=(n+3*((GetTickCount-tm)div 20)) mod 360;

      tm:=GetTickCount;
    end;
  until (tm-old>10000);


и хочется спросить где ты такую траву берешь)?

Цитата(remax @  1.12.2010,  14:33 Найти цитируемый пост)
Точность 1мс Sleep не обеспечит - обычно речь идет о 10-15 мс

Твой цикл с GetTickCount тоже не обеспечит. Ты слышал что ни будь про вытесняющую многозадачность, и планирование потоков в виндовс? Почитай) Ссылку кидать не буду ибо она прикреплена на очень видном месте)

Ах да, прочти еще раз название темы. Автор и в правду не атомные часы делает у него цель менее амбициозная.

Жаль, что приходится объяснять очевидные вещи, но видно без этого не обойтись.
1. Для отображения красивого перемещения формы необходимо обеспечить высокую степень равномерности. Очевидно?
2. Если делать ни с чем не синхронизированные задержки разной величины (от 0 до 15), то равномерности не будет. Очевидно?
3. Мой пример при незагруженном компьютере обеспечивает задержки 20мс с точностью до 1 мс. Очевидно?
4. В случае высокой загрузки компьютера возможны просчеты, которые не нарушат равномерности движения. Почему? Смотрим на код: n:=(n+3*((GetTickCount-tm)div 20)) mod 360;  Если для кого не дошло - объясняю: если  пауза прошла больше, чем рассчитано в разы, то и перемещение будет выполнено пропорционально больше. Ясно?

Кстати, не надо объяснять из каких соображений был выбран интервал 20мс?

П.С. Snowy, Вы не будете возражать, что если использовать Sleep(1000) или Sleep(1000000)  smile загрузка процессора вообще станет равной 0? Только, наверное, это не будет красивым движением формы...

П.П.С. Мне казалось, что пора менять свой старенький ноут. Но! Если он настолько быстрее работает, чем компьютер   Snowy, тонаверное, еще подожду  smile 

Автор: remax 2.12.2010, 18:39
Ага, понятно почему у Snowy такой график загрузки. На более мощном компе, действительно, пила на уровне 100% ядра (50% проца). И причина понятна - больше "холостых" циклов. Правда, непонятно почему так мало зубьев у пилы. Спишем на дискретность измерений.

Выводы:
1. Точность sleep на малых значениях никуда не годится. Можно с уверенностью говорить, что на значениях интервала меньше 10 мс он вообще практически не делает паузу (при стандартных настройках ОС). А на 20 мс он, в среднем, дает ошибку до 5% даже на "незагруженном" копьютере. Однако, если не использовать sleep, особенно на быстрых компьютерах - возможна необоснованная 100% загрузка компа.
  
2. Самый эффективный подход - мой вариант с таймером: никаких лишних действий, никакой неравномерности, никакой лишней загрузки системы. Единственное, что в нем надо для 100% правильности тоже надо привязать движение не к событиям таймера, а к GetTickCount.

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)