Delphi-PRAXiS
Seite 1 von 3  1 23      

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   GUI-Design mit VCL / FireMonkey / Common Controls (https://www.delphipraxis.net/18-gui-design-mit-vcl-firemonkey-common-controls/)
-   -   TimeSetEvent / Canvas / Thread? (https://www.delphipraxis.net/158000-timesetevent-canvas-thread.html)

HJay 1. Feb 2011 22:26

TimeSetEvent / Canvas / Thread?
 
Ich möchte eine wissenschaftliche Grafik kontinuierlich flüssig animieren und die restliche Benutzeroberfläche soll interaktiv bleiben und die Animation nicht hakeln lassen. Als Trivialversion habe ich daher erst einmal eine primitive "NewsTicker-Laufschrift" testen wollen, die echte Grafik sollte dann später leicht auszutauschen sein. Aber mir gelingt nicht einmal ansatzweise eine flüssige, pixelbasierte Animation. Wer hat Tipps, wie man da herangeht?

Ich dachte, dass ich das mit einem MultiMedia-Timer realisieren könnte.

Die Schrift springt aber um geschätzte 10 px je sichtbarer Änderung und jede Mausbewegung auf der Form lässt alles stehen. Liegt das daran, dass das ganze nicht threadsicher ist? Was mache ich so grundsätzlich falsch und wie kann man das Problem beheben? (D6 unter W7)

Code:
type
  TNewsTicker = class(TObject)
  private
    mmResult : Integer;
    Canvas  : TCanvas;
    X       : Integer;
  public
    constructor Create(ACanvas: TCanvas);
    destructor Destroy; override;
  end;

implementation

procedure TimeCallBack(TimerID, Msg: Uint; dwUser, dw1, dw2: DWORD); pascal;
  var ANewsTicker : TNewsTicker;
begin
  ANewsTicker := TNewsTicker(dwUser);
  ANewsTicker.Canvas.TextOut(ANewsTicker.X, 100, 'Test');
  Inc(ANewsTicker.X);
end;

{ TNewsTicker }

constructor TNewsTicker.Create(ACanvas: TCanvas);
begin
  inherited Create;
  Canvas  := ACanvas;
  X       := 0;
  mmResult := TimeSetEvent(50, 10, @TimeCallBack, DWORD(Self), TIME_PERIODIC);
end;

destructor TNewsTicker.Destroy;
begin
  TimeKillEvent(mmResult);
  inherited Destroy;
end;
und aufgerufen einfach durch die Form der Anwendung:

Code:
procedure TFormMain.Button1Click(Sender: TObject);
begin
  MainNewsTicker := TNewsTicker.Create(Canvas);
end;

Aremo 2. Nov 2011 10:42

AW: TimeSetEvent / Canvas / Thread?
 
Zunächst könntest Du die Gemauigkeit auf 0 ( = höchste Genauigkeit ) setzen und die Aufrufhäufigkeit auf 10 mSec

also von
mmResult := TimeSetEvent(50, 10, @TimeCallBack, DWORD(Self), TIME_PERIODIC);
auf
mmResult := TimeSetEvent(10, 0, @TimeCallBack, DWORD(Self), TIME_PERIODIC);

herabsetzen. Wenn Du die Routine nur alle 50 mSec ausführen möchtest, kannst Du intern einen Zähler von 1 bis 5 installieren und nur bei Zähler = 5 ausführen.

Dies löst aber wahrscheinlich nocht nicht das Problem, dass der Timer bei Mausbewegungen pausiert.
Dieses Pausieren habe ich bei Disk I/O auch schon feststellen müssen uns suche nach einer entsprechenden Lösung.

Medium 2. Nov 2011 10:58

AW: TimeSetEvent / Canvas / Thread?
 
Du sprichst von Threadsicher im Zusammenhang mit einem Timer. Der Timer ist aber kein Thread! Dein Vorhaben würde ich nämlich tatsächlich mit einem (echten) Thread angehen. Mal eine Pseudoklasse:
Delphi-Quellcode:
type
  TMyAnimation = class(TThread)
  private
    FBitmap: TBitmap;
    FTargetCanvas: TCanvas;
    procedure DrawFrame;
  protected
    procedure Execute; override;
  public
    constructor Create(aCanvas: TCanvas);
    destructor Destroy; override;
  end;

implementation

constructor TMyAnimation.Create(aCanvas: TCanvas);
begin
  inherited Create(false);
  FTargetCanvas := aCanvas;
  FBitmap := TBitmap.Create;
end;

destructor TMyAnimation.Destroy;
begin
  FBitmap.Free;
end;

procedure TMyAnimation.DrawFrame;
begin
  FTargetCanvas.Draw(FBitmap.Canvas, ... ...);
end;

procedure TMyAnimation.Execute;
var
  loopBeginTickCount: Int64;
const
  MS_PER_FRAME = 50;
begin
  repeat
    loopBeginTickCount := GetTickCount;
    // FBitmap mit neuem Frame bestücken, was auch immer das ist
    Synchronize(DrawFrame);
    Sleep(Max(MS_PER_FRAME - (GetTickCount-loopBeginTickCount), 1));
  until Terminated;
end;

himitsu 2. Nov 2011 10:58

AW: TimeSetEvent / Canvas / Thread?
 
Nicht die Pixel an dem ungenauen Timer-Interval richten, denn Timer-Events haben eine geringe Priorität ... also nahezu alle anderen Messages werden bevorzugt verarbeitet und ist mal der Rechner und/oder dein VCL-Thread ausgelastet, dann hängt es halt.

Ein Intervall von 50 (bis zu 20 Bilder die Sekunde) sollte ausreichen, aber dabei nicht das X um 1 hochzählen, sondern die tatsächliche Zeit, zwischen den Aufrufen oder von Beginn an, messen und davon abhängig die Position "berechnen".


[add]
Ich würde garkeine Thread verwenden.
Einfach den Timer und immer wenn Zeit ist, wird gezeichnet ... der Thread kann da auch nix machen, wenn keine Zeit vorhanden ist, da er sich ja eh synchronisieren muß.

Medium 2. Nov 2011 11:05

AW: TimeSetEvent / Canvas / Thread?
 
Ein ausgelastetes System ist ein ausgelastetes System. Klar. Aber ein Thread knallt doch regelmäßiger dazwischen als ein Timer, vor allem wenn man ihm noch ein wenig höhere Prio verpasst - falls die Animation diese Wichtigkeit hat. Streng genommen müsste man daher sogar 2 Thread nehmen: Einen, der die Frames berechnet, und einen der nur zeichnet. Da ich in obigem Beispiel aber die zum berechnen benötigte Zeit im Sleep() berücksichtige, geht das so auch schon ganz gut. (Ich hab's ziemlich genau so auch mehrfach im Einsatz. Das geht schon ganz gut, glaub mir ;))

Bummi 2. Nov 2011 11:18

AW: TimeSetEvent / Canvas / Thread?
 
Application.OnIdle ???

Medium 2. Nov 2011 11:43

AW: TimeSetEvent / Canvas / Thread?
 
Feuert auf manchen Systemen nur, wenn man die Maus über einem der Fenster bewegt, oder man im OnIdle von Hand invalidiert, was wiederum zu Hakeln in der Bedienung führen kann. Vor allem, wenn das Erstellen der Bilder etwas mehr als eine halbe Hand voll Zyklen kostet.

Bummi 2. Nov 2011 11:52

AW: TimeSetEvent / Canvas / Thread?
 
;-) nuja, wenn man Done auf false setzt nicht ....

Medium 2. Nov 2011 11:58

AW: TimeSetEvent / Canvas / Thread?
 
Okay, ja :oops: stimmt, das war damals als ich diverse Methoden probiert habe mein Fehler, Mist =) Dennoch finde ich es hübscher, möglichst wenig im Thread der GUI rennen zu lassen. Ich mag Threads mittlerweile sooo gerne, weil sie oft schon von sich aus zu schlankem und stark entkoppeltem Design zwingen. Und gerade bei so schnellen zyklischen Dingen wäre ich heilfroh das weitestgehend aus meinem GUI Kontext raus zu haben. Hat mir einfach jetzt schon zu oft das Leben leichter gemacht, als dass ich es nicht nachdrücklich empfehlen wollen würde :)

himitsu 2. Nov 2011 12:18

AW: TimeSetEvent / Canvas / Thread?
 
OnIdle wird, so wie es auch in der OH erwähnt wird, nur einmal ausgeführt, wenn alle anderen nstehenden Messages abgearbeitewt wurden.

Bewegt man die Maus ein Stückchen, dann kommen neue Messages rein (z.B. WM_MOUSEMOVE) und wurden diese wieder abgearbeitet, wird OnIdle jedes Mal erneut ausgeführt.




Wie gesagt, um in Delphi auf die VCL-Forms zu zeichnen, muß/sollte man sich im VCL-Thread befinden ... also solange man nicht zusätzliche (längere) Berechnungen im Thread hat, kommt man mit einem Timer IMHO besser.


Alle Zeitangaben in WEZ +1. Es ist jetzt 04:14 Uhr.
Seite 1 von 3  1 23      

Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz