为什么 tmediaplayer 会延迟 tpanel 上的字幕更改?
why does tmediaplayer delay a caption changing on a tpanel?
我是一名新手程序员,如果这对你们来说听起来很基础,我深表歉意。
我有一个看起来(基本上)像这样的程序:
procedure TForm1.BitBtn1Click(Sender: TObject);
begin
panel1.caption:='This is a sentence';
with MediaPlayer1 do
begin
filename:='f:\untitled.wma';
open;
wait:=true;
play;
close;
end;
end;
问题是 panel1
上的标题在媒体播放器播放完声音文件后才会改变;我需要更改标题,同时播放器开始播放。我怎样才能确保这一点?
我认为该程序会按顺序执行每一行代码,这意味着 panel1
的标题发生变化,然后媒体播放器开始运行。我哪里做错了?
设置标题后添加Refresh
调用,即:
panel1.caption:='This is a sentence';
Refresh;
解释:
VCL 组件(如 TPanel
)通常有一个称为 Invalidate()
的内部方法,当 属性(如 Caption
)发生变化并且该变化需要重新绘制时调用该方法控件的一部分(例如绘制新的标题文本)。
此方法仅在 window 控件内设置一个标志,但不会调用重绘方法本身。这样做的原因是为了避免多次调用 Repaint()
方法,如果同时更改许多属性(顺序地,在短时间内)。
当组件通过主消息循环(从应用程序的主线程 - GUI 线程处理)接收到要重绘的消息时,实际上会调用 Repaint()
方法。
您开始播放媒体播放器的方式是阻塞的,因为您将 Wait
属性 设置为 True
,这会使播放器阻塞调用线程(同样是主线程) 直到文件播放完毕。
这不会让主线程有机会处理它的消息 queue 并启动重绘。
快速修复:
问题的快速解决方法是 becsystems 建议的方法,或者这个方法:
panel1.Caption := 'This is a sentence';
Application.ProcessMessages();
调用 ProcessMessages()
将使主线程有机会处理消息 queue 并在开始播放文件之前执行更新。
这是一个快速修复,因为主线程在开始播放后仍然会被阻塞,这将阻止 window 的其他部分重新绘制(例如,尝试移动 window或在播放时将其最小化和最大化)。
becsystems 建议的代码类似,但不是处理消息 queue,而是强制控件重新绘制。
正确修复:
要正确解决此问题,您不应使用 Wait
属性 而是处理媒体播放器的 OnNotify
事件。
这是一个例子,改编自Swiss Delphi Center(未测试,因为我目前没有安装Delphi):
procedure TForm1.BitBtn1Click(Sender: TObject);
begin
panel1.Caption := 'This is a sentence';
with MediaPlayer1 do
begin
Notify := True;
OnNotify := NotifyProc;
Filename := 'f:\untitled.wma';
Open;
Play;
end;
end;
procedure TForm1.NotifyProc(Sender: TObject);
begin
with Sender as TMediaPlayer do
begin
case Mode of
mpStopped: {do something here};
end;
// Set to true to enable next-time notification
Notify := True;
end;
end;
旁注:
此处发布了 VCL 消息循环的简短说明(Delphi 开发人员指南的一部分):
另外,与问题无关,但看一下Delphi Coding Style Guide。格式化发布的代码真是太好了。
我是一名新手程序员,如果这对你们来说听起来很基础,我深表歉意。 我有一个看起来(基本上)像这样的程序:
procedure TForm1.BitBtn1Click(Sender: TObject);
begin
panel1.caption:='This is a sentence';
with MediaPlayer1 do
begin
filename:='f:\untitled.wma';
open;
wait:=true;
play;
close;
end;
end;
问题是 panel1
上的标题在媒体播放器播放完声音文件后才会改变;我需要更改标题,同时播放器开始播放。我怎样才能确保这一点?
我认为该程序会按顺序执行每一行代码,这意味着 panel1
的标题发生变化,然后媒体播放器开始运行。我哪里做错了?
设置标题后添加Refresh
调用,即:
panel1.caption:='This is a sentence';
Refresh;
解释:
VCL 组件(如 TPanel
)通常有一个称为 Invalidate()
的内部方法,当 属性(如 Caption
)发生变化并且该变化需要重新绘制时调用该方法控件的一部分(例如绘制新的标题文本)。
此方法仅在 window 控件内设置一个标志,但不会调用重绘方法本身。这样做的原因是为了避免多次调用 Repaint()
方法,如果同时更改许多属性(顺序地,在短时间内)。
当组件通过主消息循环(从应用程序的主线程 - GUI 线程处理)接收到要重绘的消息时,实际上会调用 Repaint()
方法。
您开始播放媒体播放器的方式是阻塞的,因为您将 Wait
属性 设置为 True
,这会使播放器阻塞调用线程(同样是主线程) 直到文件播放完毕。
这不会让主线程有机会处理它的消息 queue 并启动重绘。
快速修复:
问题的快速解决方法是 becsystems 建议的方法,或者这个方法:
panel1.Caption := 'This is a sentence';
Application.ProcessMessages();
调用 ProcessMessages()
将使主线程有机会处理消息 queue 并在开始播放文件之前执行更新。
这是一个快速修复,因为主线程在开始播放后仍然会被阻塞,这将阻止 window 的其他部分重新绘制(例如,尝试移动 window或在播放时将其最小化和最大化)。
becsystems 建议的代码类似,但不是处理消息 queue,而是强制控件重新绘制。
正确修复:
要正确解决此问题,您不应使用 Wait
属性 而是处理媒体播放器的 OnNotify
事件。
这是一个例子,改编自Swiss Delphi Center(未测试,因为我目前没有安装Delphi):
procedure TForm1.BitBtn1Click(Sender: TObject);
begin
panel1.Caption := 'This is a sentence';
with MediaPlayer1 do
begin
Notify := True;
OnNotify := NotifyProc;
Filename := 'f:\untitled.wma';
Open;
Play;
end;
end;
procedure TForm1.NotifyProc(Sender: TObject);
begin
with Sender as TMediaPlayer do
begin
case Mode of
mpStopped: {do something here};
end;
// Set to true to enable next-time notification
Notify := True;
end;
end;
旁注:
此处发布了 VCL 消息循环的简短说明(Delphi 开发人员指南的一部分):
另外,与问题无关,但看一下Delphi Coding Style Guide。格式化发布的代码真是太好了。