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

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

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

Delphi.int.ru Expert

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

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

#   

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


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

Подробнее »



Вопрос # 976

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

Здравствуйте!
как сделать таймер в delphi, точнее скорость изменения времени с милисекундами. реализация должна быть через функцию decode time

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

Вопрос задал: yan yan (статус: Посетитель)
Вопрос отправлен: 12 октября 2007, 17:27
Состояние вопроса: открыт, ответов: 2.

Ответ #1. Отвечает эксперт: Feniks

Здравствуйте, yan yan!
Может данная статья Вам чем-то поможет...

Компонент timer (таймер) служит для отсчета интервалов реального времени. Его свойство interval определяет интервал временив миллисекундах , который должен пройти от включения таймера до наступления события ontimer. Таймер включается при установке значения true в его свойство enabled. Единожды включенный таймер все время будет возбуждать события ontimer до тех пор, пока его свойство enabled не примет значения false.
Следует учесть, что в силу специфики реализации стандартного аппаратного таймера ibm-совместимого компьютера минимальный реально достижимый интервал отсчета времени не может быть меньше 55 мс (этот интервал называется тиком), более того, любой интервал времени, отсчитываемый с помощью таймера, всегда кратен 55 мс. Чтобы убедиться в этом, проведите эксперимент, в котором подсчитывается среднее время между двумя срабатываниями таймера (timer.dpr):
Начните новый проект с пустой формой и положите на нее компонент ttimer.
Установите в свойство enabled таймера значение false.
Приложение 1.
Необходимость нескольких (maxcount) срабатываний для точного усреднения результата связана с тем, что системные часы обновляются каждые 55 мс. После запуска программы и ввода 1 как требуемого периода срабатывания в редакторе mmoutput вы увидите строку

Задано 1 ms. Получено 55 ms.

в которой указывается, какое реальное время разделяет два соседних события ontimer. Если вы установите период таймера в диапазоне от 56 до 110 мс, в строке будет указано 110 ms и т.д. (в силу дискретности обновления системных часов результаты могут несколько отличаться в ту или иную сторону).

В ряде практически важных областей применения (при разработке игр, в системах реального времени для управления внешними устройствам и т.п.) интервал 55 мс может оказаться слишком велик. Современный ПК имеет мультимедийный таймер, период срабатывания которого может быть от 1 мс и выше, однако этот таймер не имеет компонентного воплощения, поэтому для доступа к нему приходится использовать функции api.

Общая схема его использования такова. Сначала готовится процедура обратного вызова (call back) с заголовком:
Code:
procedure timeproc(uid, umsg: uint; dwuser, dw1, dw2: dword); stdcall;

Здесь uid — идентификатор события таймера (см. об этом ниже); umsg — не используется; dwuser — произвольное число, передаваемое процедуре в момент срабатывания таймера; dw1, dw2 — не используются.
Запуск таймера реализуется функцией:

Code:
function timesetevent(udelay, uresolution: uint; lptimeproc: pointer; dwuser: dword; fuevent: uint): uint; stdcall; external 'winmm.dll';

Здесь udelay — необходимый период срабатывания таймера (в мс); uresolution — разрешение таймера (значение 0 означает, что события срабатывания таймера будут возникать с максимально возможной частотой; в целях снижения нагрузки на систему вы можете увеличить это значение); lptimeproc — адрес процедуры обратного вызова; dwuser — произвольное число, которое передается процедуре обратного вызова и которым программист может распоряжаться по своему усмотрению; fuevent — параметр, управляющий периодичностью возникновения события таймера: time_oneshot (0) — событие возникает только один раз через udelay миллисекунд; time_periodic (1) — события возникают периодически каждые udelay мс. При успешном обращении функция возвращает идентификатор события таймера и 0, если обращение было ошибочным.

Таймер останавливается, и связанные с ним системные ресурсы освобождаются функцией:

Code:
function timekillevent(uid: uint): uint; stdcall; external 'winmm.dll';

Здесь uid — идентификатор события таймера, полученный с помощью timesetevent.
В следующем примере (timer.dpr) иллюстрируется использование мультимедийного таймера Приложение 2.

Приложение:
  1.  
  2.  
  3. unit unit1;
  4.  
  5. interface
  6.  
  7. uses
  8. windows, messages, sysutils, classes, graphics, controls, forms,
  9. dialogs, stdctrls, buttons, extctrls;
  10.  
  11. type
  12. tfmexample = class(tform)
  13. panel1: tpanel;
  14. bbrun: tbitbtn;
  15. bbclose: tbitbtn;
  16. edinput: tedit;
  17. lboutput: tlabel;
  18. mmoutput: tmemo;
  19. timer1: ttimer;
  20. procedure bbrunclick(sender: tobject);
  21. procedure timer1timer(sender: tobject);
  22. procedure formactivate(sender: tobject);
  23. private
  24.  
  25.  
  26. end;
  27.  
  28. var fmexample: tfmexample;
  29.  
  30. implementation
  31.  
  32. {$r *.dfm}
  33.  
  34. procedure tfmexample.bbrunclick(sender: tobject);
  35.  
  36. var delay: word;
  37. begin
  38.  
  39. if edinput.text='' then exit;
  40. try
  41. delay := strtoint(edinput.text);
  42. except
  43.  
  44. edinput.selectall;
  45. edinput.setfocus;
  46. exit
  47. end;
  48.  
  49.  
  50.  
  51.  
  52. screen.cursor := crhourglass
  53. end;
  54.  
  55. procedure tfmexample.timer1timer(sender: tobject);
  56.  
  57.  
  58. begin
  59.  
  60.  
  61.  
  62.  
  63.  
  64. decodetime((time-begtime)/maxcount, h, m, s, ms);
  65.  
  66.  
  67.  
  68. edinput.setfocus;
  69. screen.cursor := crdefault
  70. end;
  71. end;
  72.  
  73. procedure tfmexample.formactivate(sender: tobject);
  74. begin
  75. edinput.setfocus
  76. end;
  77.  
  78. end.
  79. ///////////////////////////////////////////
  80.  
  81.  
  82.  
  83. unit unit1;
  84.  
  85. interface
  86.  
  87. uses
  88. windows, messages, sysutils, classes, graphics, controls, forms,
  89. dialogs, stdctrls, buttons, extctrls;
  90.  
  91. type
  92. tfmexample = class(tform)
  93. panel1: tpanel;
  94. bbrun: tbitbtn;
  95. bbclose: tbitbtn;
  96. edinput: tedit;
  97. lboutput: tlabel;
  98. mmoutput: tmemo;
  99. procedure bbrunclick(sender: tobject);
  100. procedure formactivate(sender: tobject);
  101. end;
  102.  
  103. var fmexample: tfmexample;
  104.  
  105. implementation
  106.  
  107. {$r *.dfm}
  108.  
  109.  
  110. function timesetevent(udelay, ureolution: uint; lptimeproc: pointer;
  111. dwuser: dword; fuevent: uint): integer; stdcall; external 'winmm';
  112.  
  113. function timekillevent(uid: uint): integer; stdcall; external 'winmm';
  114.  
  115.  
  116. var
  117.  
  118.  
  119.  
  120.  
  121.  
  122. procedure proctime(uid, msg: uint; dwuse, dw1, dw2: dword); stdcall;
  123.  
  124.  
  125.  
  126. begin
  127.  
  128.  
  129.  
  130.  
  131. decodetime((time-begtime)/maxcount, h, m, s, ms);
  132.  
  133.  
  134. [fmexample.edinput.text,ms]));
  135.  
  136. fmexample.edinput.setfocus
  137. end
  138.  
  139. ueventid := timesetevent(delay,0,@proctime,0,1);
  140. end;
  141.  
  142. procedure tfmexample.bbrunclick(sender: tobject);
  143.  
  144. begin
  145.  
  146. if edinput.text='' then exit;
  147. try
  148. delay := strtoint(edinput.text)
  149. except
  150.  
  151. edinput.selectall;
  152. edinput.setfocus;
  153. exit
  154. end;
  155.  
  156.  
  157.  
  158. ueventid := timesetevent(delay,0,@proctime,0,1);
  159. if ueventid=0 then
  160.  
  161. end;
  162.  
  163. procedure tfmexample.formactivate(sender: tobject);
  164. begin
  165. edinput.setfocus
  166. end;
  167.  
  168. end.


Ответ отправил: Feniks (статус: Бакалавр)
Время отправки: 12 октября 2007, 19:08

Ответ #2. Отвечает эксперт: min@y™

Функция DecodeTime() не имеет отношения к отсчёту времени, она лишь преобразует время из типа TDateTime (с плавающей точкой) в часы, минуты, секкунды и миллисекунды. А вот сделать таймер с точностью 1 мс возможно, сам пробовал. Я воспользовался вот этой статьёй из сброника советов Валентина Озерова:

//Для начала описываешь процедуру, которая будет вызываться по сообщению от таймера :
//--------------------------------------------------------------------------------
procedure FNTimeCallBack(uTimerID, uMessage: UINT;dwUser, dw1, dw2: DWORD); stdcall;
begin
  // Тело процедуры.
end;  
 
//а дальше в программе (например по нажатию кнопки) создаешь Таймер и вешаешь на него созданную процедуру
//--------------------------------------------------------------------------------
uTimerID:=timeSetEvent(10,500,@FNTimeCallBack,100,TIME_PERIODIC);  
 
//Подробности смотри в Help. Hу и в конце убиваешь таймер
//--------------------------------------------------------------------------------
timeKillEvent(uTimerID);  
 
//И все. Точность этого способа до 1 мсек. минимальный интервал времени можно задавать 1 мсек.
//Обратите внимание на то, что все CALLBACK-функции, вызываемые Windows, должны использовать соглашение о вызовах
stdcall.

Ответ отправил: min@y™ (статус: Доктор наук)
Время отправки: 15 октября 2007, 08:27


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

Всего сообщений: 13; последнее сообщение — 12 октября 2007, 19:41; участников в обсуждении: 2.
Вадим К

Вадим К (статус: Академик), 12 октября 2007, 17:31 [#1]:

можно сделать мультимедийный таймер, но точности сильно не добавит.
>>реализация должна быть через функцию decode time
Это как?

Может сформулируем точнее, что хотим? И не забываем, что Windows не среда реального времени
Галочка "подтверждения прочтения" - вселенское зло.
yan yan

yan yan (статус: Посетитель), 12 октября 2007, 17:40 [#2]:

необходимо создать таймер реального времени (настоящее время) с милисекундами, я его сделала. реальное время с милисекундами реализовала с помощью функции decode time. необходим еще и таймер виртуального времени, где я сама смогу изменять скорость времени - ускорять или замедлять, тоже с милисекундами
Вадим К

Вадим К (статус: Академик), 12 октября 2007, 17:52 [#3]:

Сформулируйте поточнее. я не понимаю.
что понимается под ускорением времени?
хотя может вам поможет просто банальное умножение на коэффициент? тоесть. запоминаем начальное время (переменная starttime), и текщее "виртуальное" равно
(now-starttime)*coeff
Галочка "подтверждения прочтения" - вселенское зло.
yan yan

yan yan (статус: Посетитель), 12 октября 2007, 18:03 [#4]:

при вводе определенного числа виртуальное время должно отличаться от реального времени на это число, в секундах
Вадим К

Вадим К (статус: Академик), 12 октября 2007, 18:26 [#5]:

а разве скорость и разница это одного типа величины?
подключите в uses юнит DateUtils есть функция
function IncMilliSecond(const AValue: TDateTime;
const ANumberOfMilliSeconds: Int64 = 1): TDateTime;
которое к заданному времени AValue добавит указанное кол-во милисекунд и возратит новое время
Галочка "подтверждения прочтения" - вселенское зло.
yan yan

yan yan (статус: Посетитель), 12 октября 2007, 18:38 [#6]:

у меня есть пример работы таймера, подобие которого мне надо создать, с функцией IncMilliSecond, но без милисекунд.
yan yan

yan yan (статус: Посетитель), 12 октября 2007, 18:40 [#7]:

как мне в этом коде исправить функцию IncMilliSecond, чтоб были и секунды
unit Timer;

interface

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

type
TFTimerGo = class(TForm)
RTimer: TTimer;
VTimer: TTimer;
Label2: TLabel;
Sist: TEdit;
Virt: TEdit;
Change: TEdit;
TrackBar1: TTrackBar;
RadioGroup1: TRadioGroup;
Pause: TRadioButton;
Stop: TRadioButton;
Start: TRadioButton;
Fast: TButton;
Slow: TButton;
Label1: TLabel;
Label3: TLabel;
Label4: TLabel;
Label5: TLabel;
Label6: TLabel;
procedure RTimerTimer(Sender: TObject);
procedure VTimerTimer(Sender: TObject);
procedure SlowClick(Sender: TObject);
procedure PauseClick(Sender: TObject);
procedure StopClick(Sender: TObject);
procedure StartClick(Sender: TObject);
procedure FastClick(Sender: TObject);
procedure FormCreate(Sender: TObject);
procedure TrackBar1Change(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
x,tx : TDateTime;
p,s : real;
vt,dob : integer;
end;

var
FTimerGo: TFTimerGo;
x,tx,n : TDateTime;
p,s : real;
vt,dob : integer;
r:single;

implementation

{$R *.dfm}
uses DateUtils;

procedure TFTimerGo.RTimerTimer(Sender: TObject);
begin
tx:=IncMilliSecond(tx,1000);
Sist.Text:=timetostr(tx);
RTimer.Enabled:=True;
RTimer.Interval:=1000;
end;

procedure TFTimerGo.VTimerTimer(Sender: TObject);
begin
x:= IncMilliSecond(x,dob); //Прибавление по dob секунд к х с частотой vt
Virt.Text:=timetostr(x);
end;

procedure TFTimerGo.PauseClick(Sender: TObject);
begin
VTimer.Enabled := False;
RTimer.Enabled := False;
tx:=StrToTime(Sist.Text);
end;

procedure TFTimerGo.StopClick(Sender: TObject);
begin
VTimer.Enabled:=False;
RTimer.Enabled:=False;
Sist.Text:='';
Virt.Text:='';
p:=1;
Change.Text:=FloatToStr(p);
end;

procedure TFTimerGo.StartClick(Sender: TObject);
var a:String;
begin
a:=Trim(Change.Text);
if a='' Then
begin //Проверка на ввод текста
ShowMessage('Введите ненулевое значение');
Exit;
end;

if TryStrToFloat(a,r) Then
begin
a:=FloatToStr(r);
end
else
begin
ShowMessage('Введите числовое значение');
Exit;
end;

if a[1]='-' Then
begin //Проверка знака
Delete(a,1,1);
p := StrToFloat(a);
dob:=-1000;
end
else
begin
p := StrToFloat(a);
dob := 1000;
end;

if StrToFloat(Trim(Change.Text))=0 Then
begin
ShowMessage('Введите ненулевое значение');
VTimer.Enabled := False;
RTimer.Enabled := False;
tx:=StrToTime(Sist.Text);
Exit;
end;

if (p>200) or (p<-200) Then
begin
ShowMessage('Введимое значение должно быть от -200 до 200');
Exit;
end;
if p=0 Then
begin
ShowMessage('Введите ненулевое значение');
VTimer.Enabled := False;
RTimer.Enabled := False;
tx:=StrToTime(Sist.Text);
exit;
end;
s := 1000/p;
vt := trunc(s); //Интервал виртуального таймера
VTimer.Enabled := true;
VTimer.Interval:=vt;
RTimer.Enabled := true;
RTimer.Interval:=1000;
end;

procedure TFTimerGo.FormCreate(Sender: TObject);
begin
VTimer.Enabled:=True;
RTimer.Enabled:=True;
TrackBar1.Position := 1;
x := now();
tx := now();
dob := 1000;
Rtimer.Interval := 1000;
Vtimer.Interval := 1000;
Change.Text := FloatTostr(TrackBar1.Position);
p:=1;
end;

procedure TFTimerGo.TrackBar1Change(Sender: TObject);
begin
Change.Text := FloatTostr(TrackBar1.Position);
end;

procedure TFTimerGo.FastClick(Sender: TObject);
var a:String;
begin
a:=Trim(Change.Text);
if TryStrToFloat(a,r) Then
begin
a:=FloatToStr(r);
end
else
begin
ShowMessage('Введите числовое значение');
Exit;
end;
p := StrToFloat(a);
p:=p*2;

if (p>200) or (p<-200) Then
begin
ShowMessage('Вы достигли наибольшего прибавления к виртуальному таймеру');
Exit;
end;

if p=0 Then
begin
ShowMessage('Введите ненулевое значение');
VTimer.Enabled := False;
RTimer.Enabled := False;
tx:=StrToTime(Sist.Text);
exit;
end;

Change.Text:=FloatToStr(p);
if p>0 Then
begin
dob:=1000;
end
else
begin
dob:=-1000;
end;

s:=1000/p;
vt:=trunc(s);
VTimer.Enabled := true;
VTimer.Interval:=vt;
RTimer.Enabled := true;
RTimer.Interval:=1000;

end;

procedure TFTimerGo.SlowClick(Sender: TObject);
var a:String;
begin
a:=Trim(Change.Text);

if TryStrToFloat(a,r) Then
begin
a:=FloatToStr(r);
end
else
begin
ShowMessage('Введите числовое значение');
Exit;
end;
p := StrToFloat(a);
p:=p/2;

if p>1 Then
begin
dob:=1000;
end
else
begin
dob:=-1000;
end;

if p=0 Then
begin
ShowMessage('Введите ненулевое значение');
exit;
end;
s:=1000/p;
vt:=trunc(s);
VTimer.Enabled := true;
VTimer.Interval:=vt;
RTimer.Enabled := true;
RTimer.Interval:=1000;
end;

end.
Вадим К

Вадим К (статус: Академик), 12 октября 2007, 18:57 [#8]:

во-первых, этот "таймер" будет работать плохо. дело в том, что таймер построен на базе системного таймера Windows и управляется сообщением WM_TIMER. А это сообщение может пропускаться. И это чётко описано в хелпе и мсдн. тоесть, при более-менее нагруженной системе таймер будет отставать.
во-вторых. Вот эти строки в обработчике таймера навели меня на грусть...
RTimer.Enabled:=True;
RTimer.Interval:=1000;
смысл сего действия для меня остаётся загадкой.
Если таймер сработал, то он уже запущен!
но даже если эти строки используются для запуска таймера с другой процедуры, то я бы поменял бы их местами. подумайте, почему.
в третих.
строка
if p=0 Then
это плохое сравнения. вещественные числа нельзя сравнивать на "равно"
но с другой стороны я могу ввести 0,00001 (кол-во нулей подобрать надо) и будет вам "счастя" в виде большого s. но можно и переполнение сгенерировать
дальше
if TryStrToFloat(a,r) Then
....
a:=FloatToStr(r);
....
if a[1]='-' Then
Это где такое учят??? не проще ли
if r<0 then

----------------
но какие то секунды, то миллисекунды вы ищите, я не понимаю
Галочка "подтверждения прочтения" - вселенское зло.
yan yan

yan yan (статус: Посетитель), 12 октября 2007, 19:10 [#9]:

у меня запускается реальное время,
например, 19:23:12. ввожу скорость изменения времени, к примеру 2, и виртуальное время становится 19:23:14 и продолжает "убегать" от реального времени на 2секунды. к этому всему мне необходимо добавить еще и милисекунды
Вадим К

Вадим К (статус: Академик), 12 октября 2007, 19:15 [#10]:

сделать "отображение" миллисекунд?
Галочка "подтверждения прочтения" - вселенское зло.
yan yan

yan yan (статус: Посетитель), 12 октября 2007, 19:26 [#11]:

да
Вадим К

Вадим К (статус: Академик), 12 октября 2007, 19:33 [#12]:

так надо было сначала об этом сказать.
у вас есть два пути. первый - "разложить" время на составляющие - процедура
procedure DecodeDateTime(const AValue: TDateTime; out AYear, AMonth, ADay, AHour, AMinute, ASecond, AMilliSecond: Word);
использовать где то так
var y,m,d, h, n, s, ms:word;
begin
  DecodeDateTime(Now,y,m,d, h, n, s, ms);
  Caption := format('%2d:%2d:%2d.%3d'[h, n, s, ms]);
или функцией FormatDateTime
Галочка "подтверждения прочтения" - вселенское зло.
yan yan

yan yan (статус: Посетитель), 12 октября 2007, 19:41 [#13]:

я это сделала для реального времени, а с ускорением (изменением) времени возникла проблема

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

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