Перейти к содержанию
Fire Monkey от А до Я
  • 0

Изменение текста в работающем приложении.


Андрей Рулин

Вопрос

Допустим идут вычисления, мне надо в Label выводить, что они закончились на 10%...20%...100%

После изменения текста я делаю

Label1.Repaint;
Invalidate;

При этом текст не изменяется, пока не окончатся вычисления.  Хотя в Vcl версии хватает Repaint для формы. 

Ссылка на комментарий

Рекомендуемые сообщения

  • 0

Специально для этого придумали отдельные потоки. Даже сделали несколько реализаций, которые позволяют не заводить под них отдельный класс, а описывать прямо в коде процедуры, как в javascript

Создайте поток, в нем через Synchronize() изменяйте компоненты, и все заработает

Ссылка на комментарий
  • 0

Нет, так не работает. Возможно потому, что у меня основные вычисления идут в обработчике Timer1

Помогает  добавить в обработчик

if Timer1Busy then exit;
Timer1Busy := True;

И в цикле 

 Application.ProcessMessages;

 

Ссылка на комментарий
  • 0

ProcessMessages оставим в VCL. Не будет тут так работать.

Если вычисления в таймере то оно и так нормально должно работать. Значит, что-то умалчивается.

  TForm2 = class(TForm)
    tmr1: TTimer;
    lbl1: TLabel;
    procedure tmr1Timer(Sender: TObject);
  private
  public
    pc: integer;
  end;

var
  Form2: TForm2;

implementation


{$R *.fmx}


procedure TForm2.tmr1Timer(Sender: TObject);
begin
  if pc > 10 then
  begin
    tmr1.Enabled := false;
    exit;
  end;
  lbl1.Text := format('%d%%', [pc * 10]);
  inc(pc);
end;

image.png.34c3ee351d9d5620ad68283008df5bfd.png

Вот простейший код. Все обновляется.

Ссылка на комментарий
  • 0
В 11.08.2021 в 16:43, krapotkin сказал:

Создайте поток, в нем через Synchronize() изменяйте компоненты, и все заработает

Вот кстати по этому методу я написал следующий код 


 

procedure TThreadRenew.ChangeL2;
begin
   Form2.Label2.Text:=IntToStr(CalibPercentw)+'%';
end;

procedure TThreadRenew.Execute;
begin
  while MainFlag do
    begin
      Synchronize(ChangeL2);
      //Synchronize(Form2.Invalidate);
      Sleep(1000);
    end;

end;

В Windows всё работает изменения идут ровно раз в секунду. В Андройде, увы...  Как только вычисления заканчивается, тогда да, обновляет. 

Изменено пользователем Андрей Рулин
Ссылка на комментарий
  • 0
В 02.11.2021 в 12:36, krapotkin сказал:

ProcessMessages оставим в VCL. Не будет тут так работать.

Если вычисления в таймере то оно и так нормально должно работать. Значит, что-то умалчивается.

Ну почему же, вот мы ваш код модифицируем и введём вычисления.

 

procedure TForm2.Timer1Timer(Sender: TObject);
var
i,j,k:Integer;
a:Double;
begin
  if pc >=100 then
  begin
    Timer1.Enabled := false;
    exit;
  end;
   for i := 0 to 9 do
    begin
     for j := 0 to 5000 do
       for k := 0 to 10000 do
          begin
            a:=a+k/1000+k/500;
          end;
      inc(pc);
      Label1.Text := format('%d%%', [pc]);
    end;
  //inc(pc);
end;

Он даже в Windows будет обновляться по десяткам процентов , а надо , чтобы по единицам, причём даже  если j до 10000 и более поставить, когда он не успевает завершить вычисления за отведённое время. 

Ссылка на комментарий
  • 0
1 час назад, Андрей Рулин сказал:

Он даже в Windows будет обновляться по десяткам процентов

Чтоб он писал десятками процентов надо чтоб pc изменялось по 10, смотрите кто у вас его меняет. У меня ваш код в Windows меняется по 1 проценту.

Изменено пользователем GASCHE
Ссылка на комментарий
  • 0

Вот как у вас и должно быть, чтобы по 1%.  А у меня изменяется по 10%  .  А если вы j до 10 000 или 15 000 делаете , всё равно по 10% изменяется?  Может у вас процессор быстрый?  И я, если что компилировал в 10.4. 

Изменено пользователем Андрей Рулин
Ссылка на комментарий
  • 0
32 минуты назад, Андрей Рулин сказал:

А у меня изменяется по 10%

Тогда выложите здесь исходники с тем что вы здесь написали и скомпилированный вами код, зачем гадать на гуще.

Ссылка на комментарий
  • 0

Вот такой код работает в Windows. Но в андройде , увы и ах... По 10% прибавляет.

unit Unit2;

interface

uses
  System.SysUtils, System.Types, System.UITypes, System.Classes, System.Variants,
  FMX.Types, FMX.Controls, FMX.Forms, FMX.Graphics, FMX.Dialogs,
  FMX.Controls.Presentation, FMX.StdCtrls;

type
  TForm2 = class(TForm)
    Timer1: TTimer;
    Label1: TLabel;

    procedure Timer1Timer(Sender: TObject);
    procedure FormCreate(Sender: TObject);
  private
    { Private declarations }
  public

    { Public declarations }
  end;

TThreadRenew = class(TThread)
  MainFlag : Boolean;
  constructor Create;
  Destructor Destroy;
  procedure Execute; override;
  procedure ChangeL2;
end;

var
  Form2: TForm2;
  ThreadRenew : TThreadRenew;
  pc: integer;
  Busy : Boolean = False;
implementation

{$R *.fmx}




procedure TForm2.FormCreate(Sender: TObject);
begin
   ThreadRenew := TThreadRenew.Create;
end;

procedure TForm2.Timer1Timer(Sender: TObject);
var
i,j,k:Integer;
a:Double;
begin
  if Busy then exit;
  Busy := True;
  if pc >=100 then
  begin
    Timer1.Enabled := false;
    ThreadRenew.MainFlag :=False;
  end
  else
   for i := 0 to 9 do
    begin
     for j := 0 to 5000 do
       for k := 0 to 10000 do
          begin
            a:=a+k/1000+k/500;
          end;
      inc(pc);
      Application.ProcessMessages;
    end;
  //inc(pc);
  Busy := False;
end;





constructor TThreadRenew.Create;
begin
  inherited Create(False);  //Автозапуск потока
  MainFlag := True;
end;

destructor TThreadRenew.Destroy;
begin
  inherited  Destroy;
end;

procedure TThreadRenew.ChangeL2;
begin
   Form2.Label1.Text := format('%d%%', [pc]);
  // Form2.Invalidate;
end;

procedure TThreadRenew.Execute;
begin
  while MainFlag do
    begin
      Synchronize(ChangeL2);
      //Synchronize(Form2.Invalidate);
      //Synchronize(Application.ProcessMessages);
      //if Stage>=4 then MainFlag := False
                  //else
      Sleep(100);
    end;

end;




end.

 

Ссылка на комментарий
  • 0
В 29.12.2021 в 17:01, OnePeople сказал:

ну так у вас 10 раз inc(pc)

Правильно. И после каждого inc(pc) ожидается , что будет  ОТОБРАЖАТЬСЯ +1%

В 29.12.2021 в 17:19, GASCHE сказал:

for i := 0 to 9 do begin 

уберите и будет вам счастье

Ну извините, это в тестовом коде можно убрать, а не в реальных.  Данный код , это максимальное упращение кода в сотни строк, и в это упрощении ошибка всё ещё появляется. Cуть в том, чтобы  выполнять вычисления и параллельно отображаеть ход этих вычислений. Если убрать цикл, это значит просто последовательно вести СНАЧАЛА вычисления, а ПОТОМ отображение. А если вычисления идут час. И что , мы должны час оставлять пользователя с зависшей программой? Это не дело! 

 

Наверное я очень плохо обясняю, попытаюсь то же сказать более формальный языком.

УСЛОВИЯ ТЕСТИРОВАНИЯ.  Delphi 10.4 , системы Windows , Android приведённый тестовый код.

ОЖИДАЕМЫЙ РЕЗУЛЬТАТ  для цикла for i := 0 to 9 do после КАЖДОГО изменения inc(pc);  должно отображаться изменение на 1%, например 49%...50%...51%

ФАКТИЧЕСКИЙ РЕЗУЛЬТАТ (для Android)  для цикла for i := 0 to 9 do изменения отображается ПОСЛЕ ЗАВЕРШЕНИЯ ЦИКЛА. В виде 40%...50%...60% (для всех вариантов кода). 

ФАКТИЧЕСКИЙ РЕЗУЛЬТАТ (для Windows)  cовпадает с ОЖИДАЕМЫМ РЕЗУЛЬТАТОМ для кода в посте от 29.12.2021 15:02

 

 

 

 

Изменено пользователем Андрей Рулин
уточнение
Ссылка на комментарий
  • 0

ну так я же еще раз повторяю
пока работает ваш код в основном потоке, не будет обновлений в основном потоке
выносите в другой поток
либо делите на части, чтобы по таймеру запускать каждую часть, тогда между ними будет проходить обновление

Ссылка на комментарий
  • 0
В 29.12.2021 в 15:02, Андрей Рулин сказал:

Вот такой код работает в Windows. Но в андройде , увы и ах...

По таймеру запускаете поток, в процедуре

procedure TThreadRenew.Execute;

производите расчет, когда значение внутри измениться на 1 синхронизируйтесь с основным потоком и меняете 

Label1.Text

и даже "если вычисления идут час", то через час будет вам счастье 🙂

 

Изменено пользователем GASCHE
Ссылка на комментарий
  • 0
В 10.01.2022 в 18:20, krapotkin сказал:

ну так я же еще раз повторяю
пока работает ваш код в основном потоке, не будет обновлений в основном потоке
выносите в другой поток
либо делите на части, чтобы по таймеру запускать каждую часть, тогда между ними будет проходить обновление

Там у меня выше как раз 2 разных потока. Основной код в одно, обновление в другом. 

Или вы хотите , чтобы я вычисления вынес в дополнительный поток. К сожалению так не получится, т.к. у меня в реальной задаче идёт чтение из файла вместе с вычислениями. А на адройде чтение из файла идёт только в основном потоке. Я этот вопрос уже задавал в другой ветке, пока ни у кого не получилось чтение файла в побочном потоке. 

 

В 10.01.2022 в 18:50, GASCHE сказал:

По таймеру запускаете поток, в процедуре

procedure TThreadRenew.Execute;

производите расчет, когда значение внутри измениться на 1 синхронизируйтесь с основным потоком и меняете 

Label1.Text

и даже "если вычисления идут час", то через час будет вам счастье 🙂

 

См. выше. К сожалению в побочном потоке не читаются файлы на Android. Только в основном.  Я попробую выложить тестовую задачу, где будет это видно. 

Ссылка на комментарий
  • 0
28 минут назад, Андрей Рулин сказал:

Я этот вопрос уже задавал в другой ветке, пока ни у кого не получилось чтение файла в побочном потоке.

Можно ссылку на вопрос?

Ссылка на комментарий
  • 0

В итоге решил проблему обходным путём.  Сделал проверку репаинта.

procedure TForm2.Label1Paint(Sender: TObject; Canvas: TCanvas;
  const ARect: TRectF);
begin
   RepaintFlag := True;
end;

 

В процедуре с таймером добавил.

if not RepaintFlag then exit;

 

К сожалению , как и  говорил GASCHE пришлось убрать внешний цикл и заменять на 10 вызовов по таймеру, без убирания , увы не работает. 

Ссылка на комментарий
  • 0

как то все через заднее место )

если я правильно понял то должно быть так - по таймеру запускается поток, в нем идет вычисление и синхронизация для обновления информации. 

если делать как у вас то в каждом цикле надо сообщать потоку что пора бы обновить данные. так как поток может хз когда вызваться и счетчик может уже далеко улететь. при этом если вы используете одновременно одну переменную ее надо делать потокобезопасной и блокировать. 

Ссылка на комментарий
  • 0
4 часа назад, Андрей Рулин сказал:

ну так там и написано, что все работает. и примеры есть. и у меня в приложениях файлы отлично читаются и пишутся в потоках

Ссылка на комментарий

Присоединяйтесь к обсуждению

Вы можете написать сейчас и зарегистрироваться позже. Если у вас есть аккаунт, авторизуйтесь, чтобы опубликовать от имени своего аккаунта.

Гость
Ответить на вопрос...

×   Вставлено с форматированием.   Вставить как обычный текст

  Разрешено использовать не более 75 эмодзи.

×   Ваша ссылка была автоматически встроена.   Отображать как обычную ссылку

×   Ваш предыдущий контент был восстановлен.   Очистить редактор

×   Вы не можете вставлять изображения напрямую. Загружайте или вставляйте изображения по ссылке.

  • Последние посетители   0 пользователей онлайн

    • Ни одного зарегистрированного пользователя не просматривает данную страницу
×
×
  • Создать...