Guest Posted April 1, 2019 Hi, Delphi Rio Android application.processMessages; dosent work. ProgressBar1.Value:=0; ProgressBar1.Max:=100; for I := 0 to 100 do begin ProgressBar1.Value:=I; application.ProcessMessages; sleep(100); end; trying this but progressbar not update position. any alternative way? Share this post Link to post
David Heffernan 2345 Posted April 1, 2019 Aren't you meant to do work in a background thread? 1 Share this post Link to post
DelphiMT 5 Posted April 2, 2019 (edited) I think you need to add a call to ProgressBar1.Repaint inside the loop. Edited April 2, 2019 by DelphiMT grammer Share this post Link to post
Remy Lebeau 1394 Posted April 2, 2019 (edited) This is definitely not the right way to write code. Avoid ProcessMessages() whenever possible. The example given can be replaced with a simple UI Timer instead, eg: ProgressBar1.Value : =0; ProgressBar1.Max := 100; Timer1.Enabled := True; ... procedure TForm1.Timer1Timer(Sender: TObject); begin ProgressBar1.Value := ProgressBar1.Value + 1; if ProgressBar1.Value >= ProgressBar1.Max then begin Timer1.Enabled := True; Exit; end; //... end; But, if your real project is using the ProgressBar to tracking status of actual tasks, those tasks should be done in the background, and updates to the UI synced with the main UI thread accordingly. Edited April 2, 2019 by Remy Lebeau 3 Share this post Link to post
Guest Posted April 2, 2019 Thanks for all answers, this is simple example but I dont need only progressbar, Im using application.processmessages; a lot of procedure already. Before Rio all was good for me but on Rio I have to many problems, I can not convert all application.processmessages to timer or thread need to alternative way if have. another example; on start procedure Im changing TabControl1 page (TabControl1.TabIndex:=1;) but page change after procedure completed. it didn't work like that before Rio. Share this post Link to post
Remy Lebeau 1394 Posted April 3, 2019 (edited) 59 minutes ago, pieomy said: Thanks for all answers, this is simple example but I dont need only progressbar, Im using application.processmessages; a lot of procedure already. Well, then you went down the wrong rabbit hole, and you need to start climbing your way out of it. Quote Before Rio all was good for me but on Rio I have to many problems Such as? Quote I can not convert all application.processmessages to timer or thread need to alternative way if have. You really need to stop using ProcessMessages(), and start using proper code designs. Even if you don't want to use timers/threads, there is always TThread.ForceQueue(), for instance. Anything that allows code flow to return to the main UI message loop naturally will be better than manually pumping the message queue artificially. Quote another example; on start procedure Im changing TabControl1 page (TabControl1.TabIndex:=1;) but page change after procedure completed. it didn't work like that before Rio. Then file a bug report with Embarcadero. But don't litter your code with ProcessMessages() calls just to satisfy the framework. Find other alternatives. Stay away from ProcessMessages() whenever possible. That was true even in the old days of the VCL. More so now in the days of FMX on mobile. Edited April 3, 2019 by Remy Lebeau 3 Share this post Link to post
Guest Posted April 3, 2019 @Remy Lebeau, thank you for interest I hope I can explain my problem Left gif from Tokyo, right gif from Rio on Rio application full freeze, I wanna run this procedure like tokyo Button Click; procedure TForm2.Button1Click(Sender: TObject); var I: Integer; begin TabControl1.TabIndex:=1; application.ProcessMessages; ProgressBar1.Max:=100; ProgressBar1.Value:=0; for I := 0 to 10 do begin application.ProcessMessages; ProgressBar1.Value:=(I*10); sleep(500); end; end; Share this post Link to post
Holger Flick 12 Posted April 3, 2019 19 hours ago, Remy Lebeau said: This is definitely not the right way to write code. Avoid ProcessMessages() whenever possible. The example given can be replaced with a simple UI Timer instead, eg: ProgressBar1.Value : =0; ProgressBar1.Max := 100; Timer1.Enabled := True; ... procedure TForm1.Timer1Timer(Sender: TObject); begin ProgressBar1.Value := ProgressBar1.Value + 1; if ProgressBar1.Value >= ProgressBar1.Max then begin Timer1.Enabled := True; Exit; end; //... end; But, if your real project is using the ProgressBar to tracking status of actual tasks, those tasks should be done in the background, and updates to the UI synced with the main UI thread accordingly. Remy, shouldn't it be Timer1.Enabled := False; in Timer1Timer? Otherwise, I do not understand how this code works. Example has been very helpful as I did it wrong as well so far! Share this post Link to post
Микола Петрівський 10 Posted April 3, 2019 Explanation from Marco why it does not work and they are not going to fix it: https://quality.embarcadero.com/browse/RSP-22888 1 Share this post Link to post
Remy Lebeau 1394 Posted April 3, 2019 6 hours ago, pieomy said: @Remy Lebeau, thank you for interest I hope I can explain my problem Since you are developing for mobile. DO NOT run blocking tasks in the main UI thread, it must service only the UI. Long-running tasks must be done in the background instead. In your case, you have a LONG-running task (5.5 seconds) that blocks the UI until the task is finished. Tokyo let you get away with it using a hack. Rio doesn't. So just don't do it anymore. 6 hours ago, pieomy said: on Rio application full freeze, I wanna run this procedure like tokyo You need to change the code (even for Tokyo) to work with the main UI thread correctly, stay away from ProcessMessages() at all costs (especially now that Embarcadero has broken it and doesn't want to fix it). Do things asynchronously so flow returns to the main UI message loop in a timely manner (otherwise Android is likely to kill your app!). For example: procedure TForm2.Button1Click(Sender: TObject); var StartTimer: TProc; begin ProgressBar1.Max := 100; ProgressBar1.Value := 0; StartTimer := procedure begin Timer1.Interval := 500; // <-- can be set at design-time Timer1.Enabled := True; end; {$IF CompilerVersion < 33} // 10.2 Tokyo or earlier TabControl1.TabIndex := 1; StartTimer; {$ELSE} // 10.3 Rio or later TabControl1.SetActiveTabWithTransitionAsync(TabControl1.Tabs[1], TTabTransition.None, TTabTransitionDirection.Normal, StartTimer); {$IFEND} end; procedure TForm2.Timer1Timer(Sender: TObject); begin ProgressBar1.Value := ProgressBar1.Value + 10; if ProgressBar1.Value >= ProgressBar1.Max then Timer1.Enabled := False; end; 6 hours ago, Holger Flick said: shouldn't it be Timer1.Enabled := False; in Timer1Timer? Yes, sorry about that. Fixed above. 6 hours ago, Holger Flick said: Otherwise, I do not understand how this code works. What is there not to understand? The TTabControl is transitioned to the desired Tab and a timer is started, then flow is returned to the main UI message loop. When the timer fires after 500ms, the ProgressBar is incremented and flow is returned to the main UI message loop. The timer fires again after another 500ms, and again, each time returning to the main UI message loop. Eventually, the ProgressBar reaches its Max and the timer is stopped. 1 Share this post Link to post
Rollo62 536 Posted April 4, 2019 Thanks Remy. I was always hesitating to use the "Transition" animations, because I never can be 100% sure where it may use Application.ProcessMessages internally. There are a lot of places where this is maybe used, and only by digging deep inside the sources I can check it out, but I'm never sure on what happens in future versions. procedure AnimateControlPositionXWait(AParent: TFmxObject; const NewValue: Integer); var A: TIntAnimation; begin TAnimator.StopPropertyAnimation(AParent, 'Position.X'); A := TIntAnimation.Create(AParent); try A.Parent := AParent; A.AnimationType := DefaultSlidingAnimationType; A.Interpolation := DefaultSlidingInterpoation; A.Duration := DefaultSlidingDuration; A.PropertyName := 'Position.X'; A.StartFromCurrent := True; A.StopValue := NewValue; A.Start; while A.Running do begin Application.ProcessMessages; Sleep(0); end; finally A.DisposeOf; end; end; The SetActiveTabWithTransitionAsync is maybe a good starting point for Async transitions, I will check that more deeply. TabControl1.SetActiveTabWithTransitionAsync( On the other hand I banned all unnecessary transitions from my apps anyway, because users don't feel like "cool" anymore, they often consider the UI as "slow". Share this post Link to post
Remy Lebeau 1394 Posted April 4, 2019 16 hours ago, Rollo62 said: I was always hesitating to use the "Transition" animations, because I never can be 100% sure where it may use Application.ProcessMessages internally. Doubtful, since transitions are meant to be used asynchronously. 16 hours ago, Rollo62 said: There are a lot of places where this is maybe used, and only by digging deep inside the sources I can check it out, but I'm never sure on what happens in future versions. procedure AnimateControlPositionXWait(AParent: TFmxObject; const NewValue: Integer); var A: TIntAnimation; begin TAnimator.StopPropertyAnimation(AParent, 'Position.X'); A := TIntAnimation.Create(AParent); try A.Parent := AParent; A.AnimationType := DefaultSlidingAnimationType; A.Interpolation := DefaultSlidingInterpoation; A.Duration := DefaultSlidingDuration; A.PropertyName := 'Position.X'; A.StartFromCurrent := True; A.StopValue := NewValue; A.Start; while A.Running do begin Application.ProcessMessages; Sleep(0); end; finally A.DisposeOf; end; end; TIntAnimation has an OnFinish event, you are supposed to use that instead of using a blocking wait loop on the Running property. 16 hours ago, Rollo62 said: On the other hand I banned all unnecessary transitions from my apps anyway, because users don't feel like "cool" anymore, they often consider the UI as "slow". Good thing SetActiveTabWithTransitionASync() has an option to skip the actual transition. Share this post Link to post
Holger Flick 12 Posted April 14, 2019 On 4/3/2019 at 4:11 PM, Remy Lebeau said: What is there not to understand? The TTabControl is transitioned to the desired Tab and a timer is started, then flow is returned to the main UI message loop. When the timer fires after 500ms, the ProgressBar is incremented and flow is returned to the main UI message loop. The timer fires again after another 500ms, and again, each time returning to the main UI message loop. Eventually, the ProgressBar reaches its Max and the timer is stopped. You fixed the mistake. I was unclear if you were right, and then I was not able to understand how it should have worked. After the fix, I understand the code. Share this post Link to post
Guest Posted April 16, 2019 Thank you for all answers, SetActiveTabWithTransitionASync working good. Share this post Link to post