Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Sonstige Fragen zu Delphi (https://www.delphipraxis.net/19-sonstige-fragen-zu-delphi/)
-   -   Delphi Gespräch simulieren?? (https://www.delphipraxis.net/168695-gespraech-simulieren.html)

thechus 4. Jun 2012 20:56

Gespräch simulieren??
 
Liebes Forum,

ich möchte, dass Gesprächstexte zeitlich versetzt auf verschiedenen Panels erscheinen.
Leider bekomme ich das nicht hin :cry:
Mein Ansatz ist folgender:

Code:
procedure TJesusS.Dialog;
var n :integer;
begin
 panel1.visible := true;
 panel2.visible := true;
 n := 0;
 repeat
 sleep(100);
 n := n + 1;
 if n = 1 then panel1.caption := 'HA!';
 if n = 10 then panel2.caption := 'HA!';
 until n = 10;
end;
Leider führt er alle Befehle gleichzeitig aus :cry:
Ich hab's auch schon mit einem Timer versucht.
Da kam das selbe bei raus.

Ich hoffe ihr könnte mir helfen...

Danke und Gruß,
thechus

haentschman 4. Jun 2012 21:09

AW: Gespräch simulieren??
 
nun ja... bei Sleep 100ms und 10 Durchläufen ist nach 1s alles fertsch...8-) Für dich sieht es dann nach alles auf einen Blick aus, da innerhalb der Schleife keine Nachrichten abgearbeitet werden und somit erst danach alles gezeichnet wird (Stichwort: ApplicationProcessMessages).

Versuche noch einmal dein Vorhaben detailierter zu beschreiben.

Furtbichler 4. Jun 2012 21:17

AW: Gespräch simulieren??
 
Es reicht nicht, per "Panel1.Caption := ... " dem Panel zu sagen, welcher Text dort erscheinen soll, man muss dem Panel (bzw. Windows) auch ne Chance geben, das mal zu zeichnen.
Stichwort "Application.ProcessMessages".

thechus 4. Jun 2012 21:18

AW: Gespräch simulieren??
 
Hmm okay...

Also.. wenn ich die Zeiten verlängere, taucht die Schrift einfach nur viel später auf. Trotzdem beide gleichzeitig.

Ich habe folgendes vor:
Ich habe einen kleinen Kampf simuliert.
Am ende bleiben 2 Kämpfer übrig und Jesus taucht auf.
Und jetzt möchte ich ein Gespräch zwischen den beiden Kämpfern und Jesus programmieren.
Die Lösung die mir dazu einfiel war, dass ich zu jeder Figur einen Panel habe, auf dem zeitlich versetzt die Gesprächszeilen rechtzeitig ei- und ausgeblendet werden.

Das "zeitlich versetzt" bekomme ich nicht hin. :cry:

Ich hoffe, dass das genug Info ist,

danke und Gruß,
thechus

thechus 4. Jun 2012 21:19

AW: Gespräch simulieren??
 
Na gut danke.
Ich mach mich da mal schlau.:shock:

thechus 4. Jun 2012 21:24

AW: Gespräch simulieren??
 
Ahh :D:D

Sauber hat geklappt! Danke :-D:-D

Hier der Code für Leute, die das selbe Problem haben:

Code:
procedure TJesusS.Dialog;
var n :integer;
begin
 panel1.visible := true;
 panel2.visible := true;
 n := 0;
 repeat
 sleep(1000);
 n := n + 1;
 Application.ProcessMessages;
 if n = 1 then panel1.caption := 'HA!';
 if n = 10 then panel2.caption := 'HA!';
 until n = 10;
end;
Vielen Dank!
Das ist Hilfe, wie ich mir sie vorstelle.

Gruß,
thechus

himitsu 4. Jun 2012 21:25

AW: Gespräch simulieren??
 
Mach dich auch besser mal über Timer schlau.
Delphi-Referenz durchsuchenTTimer

thechus 4. Jun 2012 21:39

AW: Gespräch simulieren??
 
Ja Chef! :thumb:

Romiox 4. Jun 2012 23:04

AW: Gespräch simulieren??
 
Ist jetzt vielleicht mehr so ne Kleinigkeit, aber schöner fänd ich ja, wenn du das neuzeichnen veranlassen würest nachdem du neue Captions setzt, und nicht davor. Das juckt in der Schleife vielleicht nicht, aber mich irgendwie :)

himitsu 5. Jun 2012 07:47

AW: Gespräch simulieren??
 
Zitat:

Zitat von Romiox (Beitrag 1169525)
Ist jetzt vielleicht mehr so ne Kleinigkeit, ...

Gut, hier sind die Ausgaben dadurch um 100 1000 Millisekunden verzögerst.

Abgesehn davon, daß weder Schleife noch ProcessMessages nötig sind.

WladiD 5. Jun 2012 09:28

AW: Gespräch simulieren??
 
Ihr solltet aufhören den Einsatz von Application.Processmessages zu empfehlen!

In einem Test-Projekt ist der Einsatz noch ok, aber wenn sich zu viele Application.Processmessages in einem Produktivsystem ansammeln, so hat das allerlei Seiteneffekte, die nicht nachvollziehbar und sehr schwierig zu debuggen sind.

Stellt euch doch einfach mal vor, was der Aufruf von Application.Processmessages alles ausführen kann: Timer werden abgearbeitet, Thread-Synchronisierung (TThread.Synchronize) wird durchgeführt, kurz gesagt alle Messages, und das in irgendeinem Event-Handler?!

Da hat Borland seinerzeit definitiv einen Designfehler gemacht, TApplication.Processmessages hätte protected sein müssen.

Ich arbeite an einer historisch gewachsenen Anwendung, die noch vor einem Jahr über 150 Aufrufe dieser Methode hatte, nunja, die Anwendung war in vielen Fällen einfach unberechenbar. Mit jedem Release versuche ich es Stück für Stück zu entfernen und es läuft immer besser.

himitsu 5. Jun 2012 09:50

AW: Gespräch simulieren??
 
Zitat:

wirre Seitenefekte
Wo ich nur zustimmen Kann.
http://www.delphipraxis.net/167456-p...dows-test.html

Iwo Asnet 5. Jun 2012 12:11

AW: Gespräch simulieren??
 
Ich glaube immer noch fest daran, das Fragen direkt beantwortet werden sollten und Tipps, wie man es besser machen kann, als unverbindliche Empfehlung zusätzlich dargeboten werden sollten.

Die Eingangsfrage war klar: "Hier ist mein Code, der klappt nicht. Wieso?"
Antwort: "Nimm Application.ProcessMessages"

Trotzdem sind die weitergehenden Diskussionen und Anmerkungen, insbesondere die über den schlechten Stil in Bezug auf die ProcessMessages, durchaus sehenswert und sehr interessant.

Persönlich halte ich nicht viel davon, Methoden, mit denen man Schindluder treiben kann, in die 'protected' Ecke zu verbannen. Das müsste man dann auch mit einem Gaspedal beim Auto machen :stupid:

Valle 5. Jun 2012 20:44

AW: Gespräch simulieren??
 
Zitat:

Zitat von Iwo Asnet (Beitrag 1169568)
[...] das Fragen direkt beantwortet werden sollten [...]

Die Eingangsfrage war klar: "Hier ist mein Code, der klappt nicht. Wieso?"
Antwort: "Nimm Application.ProcessMessages"

Die Eingesfrage war nicht warum. Eigentlich war gar keine Frage vorhanden. Interessanter finde ich eher diesen Teil des Ausgansposts:

Zitat:

Zitat von thechus (Beitrag 1169514)
Leider führt er alle Befehle gleichzeitig aus :cry:
Ich hab's auch schon mit einem Timer versucht.
Da kam das selbe bei raus.

Also wenn man es macht wie Iwo Asnet es meint, dann sollte geklärt werden, dass
  • Windows hier definitiv nichts gleichzeitig ausführt
  • warum sein gezeigter Code nicht klappt
  • dass sein Timer-Versuch wohl fehlerhaft gewesen sein muss
  • und was die richtige Lösung des Problems ist

Und wenn man will, kann man auch den Hack Application.ProcessMessages erwähnen. Natürlich ist es eine Sache des Antwortenden, ob er Lust und Zeit hat, dem TE breit zu erklären warum das alles so ist. Immerhin ist das System für einen nicht-eingeweihten nicht so einfach zu verstehen. Aber besonders der Timer gehört zu den Grundlagen, die man als bessere Alternative zur genannten Methode durchaus darlegen darf. ;-)

Liebe Grüße,
Valentin

Furtbichler 6. Jun 2012 07:16

AW: Gespräch simulieren??
 
Zitat:

Zitat von Valle (Beitrag 1169620)
  • Windows hier definitiv nichts gleichzeitig ausführt
  • warum sein gezeigter Code nicht klappt
  • dass sein Timer-Versuch wohl fehlerhaft gewesen sein muss
  • und was die richtige Lösung des Problems ist

Die Panels werden erst beim nächsten Abarbeiten der anstehenden Messages neu gezeichent => Windows führt die Befehle scheinbar gleichzeitig aus.
Durch den Aufruf von ProcessMessages klappt der gezeigte Code (Hilfe vollständig gegeben), der Timer-Versuch ist nicht dokumentiert und
die 'richtige' Lösung ist abhängig vom Problem und der Fragestellung.

Hmmm. Was wolltest Du nochmal? ;-)

Ich habe die Frage auch so verstanden, das er wissen wollte, wieso sein Code nicht klappt. Dessenungeachtet ist das hier mal wieder Wortklauberei.

Ehrlich gesagt halte ich es für durchaus legitim, in einem Button-Event, der etwas länger dauert, ein Application.ProcessMessages aufzurufen.
Wenn ich z.B. einen Batch starte, der z.B. 10000 Datensätze importiert, zeige ich Fortschritt, verstrichene Zeit, usw. innerhalb der Schleife an und rufe dann Application.ProcessMessages auf.
Wieso sollte man das anders machen? Es ist straight forward und reicht vollkommen aus.
Ich vermute, die weitaus meisten Fortschrittsanzeigen sind ähnlich aufgebaut.
Insofern verstehe ich nicht, wieso der Aufruf von ProcessMessages ein Hack sein soll.
Klar, man kann den ganzen Schmoo in einen Thread auslagern.
Klar, man kann die Anzeige über einen Timer lösen.
Klar, man kann endlos darüber diskutieren.
Aber warum?

Thom 6. Jun 2012 09:28

AW: Gespräch simulieren??
 
Es geht nicht darum, ewig zu diskutieren, sondern nur um die hier im Forum übliche "Meine Sicht ist die einzig wahre..."-Streiterei.

WladiD hat vollkommen Recht: Der Aufruf von Application.ProcessMessages kann zu schwer lokalisierbaren Zugriffsverletzungen führen. Dazu gab es erst vor kurzem eine Diskussion.
Manche Probleme lassen sich allerdings nur unzureichend ohne den Einsatz von Application.ProcessMessages lösen, da damit nicht nur Nachrichten abgearbeitet, sondern zum Teil auch Rechenzeit abgegeben wird, was für manche Aufgaben äußerst wichtig ist. Allerdings sollten dann auch entsprechende Sicherheitsmaßnahmen getroffen werden.
Wenn es nur darum geht, ein Steuerelement zu aktualisieren, reicht der Aufruf der Methode Update vollkommen aus, ist ebenfalls in einer Zeile erledigt und birgt nicht die mit Application.ProcessMessages verbundenen Risiken:
Delphi-Quellcode:
procedure TForm1.Button1Click(Sender: TObject);
var
  n: Integer;
begin
  for n:=0 to 10 do
  begin
    Label1.Caption:=IntToStr(n);
    Label1.Update;
    Sleep(200);
  end;
end;

MrSpock 6. Jun 2012 09:37

AW: Gespräch simulieren??
 
Ist das tatsächlic so, dass der Aufruf von Update ausreicht? Ich hätte vermutet, dass Update nur eine Nachricht an das entsprechende Element schickt, die aber ggf. nur abgearbeitet wird, wenn Windows "Zeit" dafür hat und explizit mit Application.ProcessMessages dazu aufgefordert wird. In deinem Beispiel funktioniert das wohl, aber grundsätzlich ist das kein Ersatz, oder? :?

himitsu 6. Jun 2012 09:43

AW: Gespräch simulieren??
 
Update ruft standardmäßig direkt/sofort MSDN-Library durchsuchenUpdateWindow auf.

Bei Update, Refresh und Repaint kommt aber letzendlich darauf an, wie bei dem Control diese Methoden implementiert sind. (also ob und wie sie überschrieben/verändert wurden)

DeddyH 6. Jun 2012 09:44

AW: Gespräch simulieren??
 
Update veranlasst den Parent, UpdateWindow auszuführen. Dazu sagt das MSDN:
Zitat:

The function sends a WM_PAINT message directly to the window procedure of the specified window, bypassing the application queue.
Das bedeutet im Umkehrschluss, dass lediglich das Control neu gezeichnet, aber keine anderen Nachrichten abgearbeitet werden. Die angesprochenen Seiteneffekte treten somit nicht auf, man kann also z.B. auch das Fenster nicht verschieben etc.

Progman 6. Jun 2012 11:45

AW: Gespräch simulieren??
 
Betreffs des Eingangsproblems:
Man könnte das durchaus mit einem Timer sehr elegant lösen.
Man nimmt sich eine (integer) Zählvariable, die auf Null gesetzt wird.
Diese wird im Timer-Ereignis jedesmal erhöht.
Je nach ihrem Wert kann in einer Case-Anweisung bestimmt werden, was passieren soll.
Ich nenne sie hier mal Phase.

Delphi-Quellcode:
procedure TfrmMain.Timer1Timer(Sender: TObject);
begin
  Timer1.Enabled:=False;
  case Phase of
    1: begin
         //erstes Panel beschriften, sichtbar, Refresh
         Timer1.Interval:=1500; //1,5 sek
       end;
    2: begin
         //zweites Panel beschriften, sichtbar, Refresh
         Timer1.Interval:=1000; //1 sek
       end;
    3: begin
         //drittes Panel beschriften, sichtbar, Refresh
         Timer1.Interval:=2000; //2 sek
       end;
    //beliebig erweiterbar....
  end;
  inc(Phase);
  if Phase < 4 then //maximalwert der Phase + 1, ansonsten wird der Timer nicht mehr enabled
   Timer1.Enabled:=True; //nächsten Intervall durchlaufen
end;
So kann man beliebige Aktionen in einer festlegbaren Reihenfolge ausführen.

himitsu 6. Jun 2012 12:24

AW: Gespräch simulieren??
 
Ich würde aber nicht ständig am Timer rumspielen.
Jedesmal wenn das Intervall geändert wird, oder wenn man das Enabled ändert, wird intern ein verstecktes Fenster erstellt und darauf ein Timer erzeugt. (bei Änderungen wird vorher alles wieder gelöscht)

z.B. Timer.Intervall auf 1000 und dann entspricht jede Phase der abgelaufenen Sekunde.
Delphi-Quellcode:
procedure TfrmMain.Timer1Timer(Sender: TObject);
begin
  case Phase of
    1: begin
         //erstes Panel beschriften, sichtbar, Refresh
       end;
    5: begin
         //zweites Panel beschriften, sichtbar, Refresh
       end;
    17: begin
         //drittes Panel beschriften, sichtbar, Refresh
       end;
    x: begin
         //beliebig erweiterbar
         
         Timer1.Enabled := False;//und beim Letzen den timer ausschalten
       end;
  end;
  inc(Phase);
end;

Thom 6. Jun 2012 13:27

AW: Gespräch simulieren??
 
@Progman:
Ja, ganau: Das wäre eine universelle Lösung, bei der der Rest der Anwendung auch noch bedienbar bleibt und sogar geschlossen werden könnte, ohne Zugriffsverletzungen durch Application.ProcessMessages befürchten zu müssen.

@MrSpock:
Die Methode Update greift nur, wenn das Steuerelement komplett oder zumindest der betreffende Teil in die Updateregion aufgenommen wurde. Beim Label geschieht das automatisch, wenn die Beschriftung geändert wurde. Anders sieht es bei selbstgezeichneten Elementen - wie zum Beispiel einer PaintBox aus. Hier muß der Bereich explizit zur Updateregion hinzugefügt werden. Der Einfachheit halber kann man das über die Methode Invalidate bewerkstelligen:
Delphi-Quellcode:
  PaintBox1.Invalidate;
  PaintBox1.Update;
Beide Anweisungen lassen sich auch mit
Delphi-Quellcode:
PaintBox1.Repaint
zusammenfassen.
Ein kleines Beispiel dazu (ein Button und eine PaintBox auf dem Formular und die Unit AnonymousMethodsTools.pas aus dem Google Maps Framework einbinden):
Delphi-Quellcode:
procedure TForm1.Button1Click(Sender: TObject);
const
  Radius = 5;
var
  x: Integer;
begin
  with PaintBox1 do
  begin
    OnPaint:=MakeEvent(
      procedure(Sender: TObject)
      var
        y: Integer;
      begin
        y:=Round(-4*(Radius-Height)/Sqr(Width)*Sqr(x-Width/2)+Radius);
        Canvas.Brush.Color:=clRed;
        Canvas.Ellipse(x-Radius,y-Radius,x+Radius,y+Radius);
      end);
    for x:=-Radius to Width+Radius do
    begin
      Repaint;
      Sleep(10);
    end;
    ReleaseEvent(OnPaint);
    OnPaint:=nil;
  end;
end;
Hier wird einfach eine rote Kugel (genauer gesagt: ein Kreis) in einer parabelförmigen Bahn durch die Gegend geschossen. Auch hier werden die Veränderungen auf dem Bildschirm sichtbar, obwohl ProcessMessages nicht aufgerufen wird. Allerdings ist die Anwendung in dieser Zeit natürlich nicht bedienbar.

Ersetzt man jetzt die Schleife mit
Delphi-Quellcode:
    [...]
    for x:=-Radius to Width+Radius do
    begin
      Invalidate;
      Application.ProcessMessages;
      Sleep(10);
    end;
    [...]
kommt es zu Zugriffsverletzungen, da jetzt der Anwender die Möglichkeit hat, den Button während der Ausführung der Methode erneut zu betätigen. Damit sind dann zusätzliche Sicherungsmaßnahmen notwendig (zum Beispiel Button während der Ausführung deaktivieren oder ein Flag mit Abbruchbedingung).

@himitsu:
Wo steht das mit dem internen Fenster?
Im Quelltext von TTimer.UpdateTimer in der Unit ExtCtrls steht lediglich ein Aufruf von KillTimer und SetTimer (beides API-Funktionen) mit dem selben Fenster-Handle...

himitsu 6. Jun 2012 13:38

AW: Gespräch simulieren??
 
Schau dir mal das Handle an, welches für KillTimer/SetTimer verwendet wird. :zwinker:

Das Problem dabei ist, daß Timer über eine ID direkt auf einem Formular sitzen. Leider gibt es keine Möglichkeit, um rauszufinden ob und welche IDs belegt sind.
Darum wurde hier einfach für jeden Timer ein eine eigene Form genommem und darauf wird dann stur mit der ID 1 gearbeitet.

[edit]
Komisch, mir war so, als wenn das Fenster freigegeben wird, wenn es nicht mehr nötig ist. :gruebel:
OK, dann isses doch nicht so schlimm.
Aber wenn man die Zeit halbwegs synchron halten will, wäre es dennoch nicht so schlecht, den Timer laufen zu lassen.
- Um die Zeit, welche vom Auslösen des Timer-Events bis zu dessen Bearbeitung vergangen ist und dazu noch die Zeit zwischen Diasable und Enable (des Timers) wird der nächste Aufruf verzögert.

Progman 6. Jun 2012 13:57

AW: Gespräch simulieren??
 
Zitat:

Zitat von himitsu (Beitrag 1169697)
.....
Aber wenn man die Zeit halbwegs synchron halten will, wäre es dennoch nicht so schlecht, den Timer laufen zu lassen.
- Um die Zeit, welche vom Auslösen des Timer-Events bis zu dessen Bearbeitung vergangen ist und dazu noch die Zeit zwischen Diasable und Enable (des Timers) wird der nächste Aufruf verzögert.

Das zwischenzeitliche Disablen des Timers hat eigentlich nur den Zweck, eine eventuell länger dauernde Befehlsfolge/Abarbeitung im Case-Block zu berücksichtigen, um zu verhindern, dass der nächste Timer-Event zu früh eintritt. Das betrifft aber nur Befehlsfolgen, die wirklich länger dauern als der Timer-Interval, was eigentlich nur selten vorkommen sollte.
Es könnte im hier zutreffenden Fall durchaus weggelassen werden :)


Alle Zeitangaben in WEZ +1. Es ist jetzt 18:30 Uhr.

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