Delphi 类似“闹钟”的应用程序

发布于 2024-12-19 02:32:23 字数 499 浏览 7 评论 0原文

我需要制作一个简单的闹钟应用程序,它不是播放声音,而是将文件上传到 ftp(弄清楚了后者)。 事实证明,在执行线程时,计时器是无效的

到目前为止,我得到的是:

var
ttime     : tDateTime;
timerstr  : string;
timealarm : string;
aThread : TMyThread;
begin
aThread := tMyThread.Create(false);
ttime := Now;
timestr := FormatDateTime('hh:nn:ss', ttime);
timealarm := '09:30:30';
if timestr = timealarm then
aThread.Resume; //The thread will execute once and terminate;
end;

你们能想出另一种方法来使该命令以更有效的方式每天发生一次吗?

谢谢。

I need to make a simple alarm clock application that, instead of playing a sound, will upload a file to the ftp (got the latter figured out). Timers have proved to be ineffective when it comes to executing a thread.

Here's what I got so far:

var
ttime     : tDateTime;
timerstr  : string;
timealarm : string;
aThread : TMyThread;
begin
aThread := tMyThread.Create(false);
ttime := Now;
timestr := FormatDateTime('hh:nn:ss', ttime);
timealarm := '09:30:30';
if timestr = timealarm then
aThread.Resume; //The thread will execute once and terminate;
end;

Can you guys think of another way to make that command happen once a day in a more effective way?

Thank you.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

极度宠爱 2024-12-26 02:32:23

找到解决方案CRON Scheduler

谢谢 LU RD和跑步者。

Solution found: CRON Scheduler

Thank you LU RD and Runner.

初见你 2024-12-26 02:32:23

这是我的示例代码。需要注意的是,比较是在 TDateTime 值之间而不是字符串之间进行的,比较是 >= 而不是 =,当我不想要它,我正在跟踪它运行的最后一天。

procedure TForm1.Timer1Timer(Sender: TObject);
var
  currentTime: TDateTime;
  alarmTime: TDateTime;
begin
  // Time is a floating point value with everything to the left of the zero
  // representing the days, and everything to the right of the decimal
  // point representing time of day

  // Date() gets just the day portion, and lastDateExecuted is a global TDateTime
  // variable where we store the last day the program ran
  // We only go further if it didn't execute today
  if (Date > lastDateExecuted) then
  begin
    // Use Time() instead of Now() to get just the time portion and leave the day
    // portion at 0
    currentTime := Time;
    // Covert our alarm string to TDateTime instead of TDateTime to string
    alarmTime := EncodeTime(9, 30, 30, 0);
    // Is it time to run?
    // Greater than or equal comparison makes the application still run in case
    // our timer happens to miss the exact millisecond of the target time
    if currentTime >= alarmTime then
    begin
      // Immediately set the last run date to keep the next timer event
      // from repeating this
      lastDateExecuted := Date;
      // Do your work here
      DoMyThing;
    end;
  end;
end;

请务必将 lastDateExecuted 值初始化为 0 之类的值。

根据您的粒度使用计时器间隔。如果您希望它在目标时间的一分钟内运行,请将间隔设置为一分钟。如果您希望它尝试在一秒钟内运行,请将计时器间隔设置为一秒。

Here's my sample code. It's important to note that the comparison is between TDateTime values instead of strings, the comparison is >= rather than =, I'm careful to exclude the day portion when I don't want it, and I'm keeping track of the last day it ran.

procedure TForm1.Timer1Timer(Sender: TObject);
var
  currentTime: TDateTime;
  alarmTime: TDateTime;
begin
  // Time is a floating point value with everything to the left of the zero
  // representing the days, and everything to the right of the decimal
  // point representing time of day

  // Date() gets just the day portion, and lastDateExecuted is a global TDateTime
  // variable where we store the last day the program ran
  // We only go further if it didn't execute today
  if (Date > lastDateExecuted) then
  begin
    // Use Time() instead of Now() to get just the time portion and leave the day
    // portion at 0
    currentTime := Time;
    // Covert our alarm string to TDateTime instead of TDateTime to string
    alarmTime := EncodeTime(9, 30, 30, 0);
    // Is it time to run?
    // Greater than or equal comparison makes the application still run in case
    // our timer happens to miss the exact millisecond of the target time
    if currentTime >= alarmTime then
    begin
      // Immediately set the last run date to keep the next timer event
      // from repeating this
      lastDateExecuted := Date;
      // Do your work here
      DoMyThing;
    end;
  end;
end;

Be sure to initialize the lastDateExecuted value to something like 0.

Use the timer interval for your granularity. If you want it to run within a minute of the target time, set the Interval to a minute. If you want it to try to run within a second, set the timer interval to a second.

撩人痒 2024-12-26 02:32:23

如果我理解正确的话,您需要知道的是如何识别何时达到特定时间以便执行该线程。事实上,计时器不一定是用于此目的的理想工具,因为计时器实际上并不实时触发(您可以将间隔设置为 500 毫秒,但在 1 分钟或 60,000 毫秒后,它不会如您所愿,完美排列到精确的 120 次执行)。然而,这并不意味着我们不能使用计时器。

在计时器内(或者您也可以为此创建另一个重复线程),您只需获取当前日期/时间即可。重要提示:确保此计时器或线程的间隔小于半秒 - 这将确保您的时间不会被错过。所以你会这样读它......

uses
  DateUtils;

.....

var
  HasExecuted: Bool;

.....

constructor TForm1.Create(Sender: TObject);
begin
  HasExecuted:= False;
end;

procedure TimerOnTimer(Sender: TObject);
var
  N, A: TDateTime;
  Thread: TMyThread;
begin
  N := Now;
  A := StrToDateTime('11/20/2011 15:30:30'); //24 hour time
  //If now is within 1 second over/under alarm time...
  if (N >= IncSecond(A, -1)) and (N <= IncSecond(A, 1)) then
  begin
    if not HasExecuted then begin
      HasExecuted:= True;
      aThread := tMyThread.Create(true);
      aThread.Resume; //The thread will execute once and terminate;  
    end;
  end;
end;

它可能不适合你,因为你正在使用 = 运算符。如果这个计时器跳过那 1 秒怎么办?一次执行可能是前第二次执行,下一次执行可能是第二个答案,在这种情况下,它不会将此表达式计算为等于 true。

另一方面,您的 TMyThread 构造函数 - 您是否重写了它?如果是这样,False 仍然是原来的 CreateSuspished 参数吗?如果是这样,那么您就是在告诉该线程在创建后立即执行。在此参数中传递 True 使其在创建时暂停,因为我看到您还在该参数下面调用 Thread.Resume (如果参数保持 false,则它已经恢复) 。另一方面,除非时间已到,否则您也不需要创建该线程(至少我假设)。为什么在检查之前就创建它?每次执行该计时器时,它都会创建其中一个(我假设这是根据需要负责上传的线程)。另外,请确保线程在完成后正确地释放自身,而不仅仅是卡住......

constructor TMyThread.Create(CreateSuspended: Bool);
begin
  inherited Create(CreateSuspended);
  FreeOnTerminate:= True; //Make sure it free's its self when it's terminated
end;

编辑:

我错过了一些东西 - 如果,比方说,这个间隔timer 为 1(应该更像是 200-400),那么在这段时间内它可以很好地连续执行多次。我修改了上面的代码以确保它只执行一次。注意:此代码是凭记忆键入的,而不是在 delphi 环境中键入的。

If I understand you right, all you need to know is how to recognize when a particular time has been reached in order to execute this thread. Indeed, a timer isn't necessarily an ideal tool to use for this, as timers don't actually trigger in real-time (you can have the interval on 500 msec, but over after 1 minute, or 60,000 msec, it will not be perfectly lined up to an exact 120 executions, as you would wish). However, that doesn't mean we can't use timers.

Inside the timer (or you can make another repeating thread for this too), you simply get the current date/time. IMPORTANT: Make sure the interval of this timer or thread is less than half a second - this will ensure that your time won't be missed. So you would read it like this...

uses
  DateUtils;

.....

var
  HasExecuted: Bool;

.....

constructor TForm1.Create(Sender: TObject);
begin
  HasExecuted:= False;
end;

procedure TimerOnTimer(Sender: TObject);
var
  N, A: TDateTime;
  Thread: TMyThread;
begin
  N := Now;
  A := StrToDateTime('11/20/2011 15:30:30'); //24 hour time
  //If now is within 1 second over/under alarm time...
  if (N >= IncSecond(A, -1)) and (N <= IncSecond(A, 1)) then
  begin
    if not HasExecuted then begin
      HasExecuted:= True;
      aThread := tMyThread.Create(true);
      aThread.Resume; //The thread will execute once and terminate;  
    end;
  end;
end;

It probably doesn't work right for you because you are using a = operator. What if this timer skips that 1 second? One execution could be the second before, and the next execution could be the second answer, in which case it won't evaluate this expression to equal true.

On another note, your TMyThread constructor - are you overriding this? If so, is the False still the original CreateSuspended parameter? If so, then you are telling this thread to execute immediately upon creation. Pass True in this parameter to make it suspended upon creation, because I see you are also calling Thread.Resume below that (which if the parameter remains false, it's already resumed). On the other hand, you also do not need to create that thread (at least I'm assuming) unless the time has been reached. Why are you creating it before you even check? It's creating one of these for each and every time this timer is executed (I'm presuming this is the thread that will take care of the uploading, as needed). Also, make sure that the thread is properly free'ing its self when it's done, and doesn't just get stuck...

constructor TMyThread.Create(CreateSuspended: Bool);
begin
  inherited Create(CreateSuspended);
  FreeOnTerminate:= True; //Make sure it free's its self when it's terminated
end;

EDIT:

I missed something - if, let's say, the interval of this timer was 1 (it should be more like 200-400), then it could very well execute many times in a row, during this time period. I modified the code above to also make sure it's only executed once. NOTE: This code was typed by memory, not in a delphi environment.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文