ปุ่มไม่ตอบสนองในแบบฟอร์มที่แสดงในขณะที่แอปพลิเคชันไม่ว่าง

ฉันมีแบบฟอร์มรองที่แสดงขึ้นในขณะที่กำลังประมวลผลอย่างหนักในฟอร์มหลัก
ฉันส่งข้อความไปยังฟอร์มรอง (form2) เกี่ยวกับความคืบหน้าของการประมวลผล - ซึ่งทำงานได้ดี
ฉันต้องการปุ่มบน form2 เพื่อ ยกเลิกการประมวลผลโดยการปิด form2 และตั้งค่าตัวแปรโกลบอลให้เป็นเท็จอีกครั้ง ไม่มีปุ่มใดทำงานบน form2 หากเปิดด้วย form2.show (เมื่อคลิกและเมาส์ลงไม่ทำอะไรเลย และปุ่มไม่ขยับ)
ปุ่มเหล่านั้นทำกับ form2.showmodal แต่จะหยุดการประมวลผลใดๆ ใน Mainform และยังหยุดมองเห็นหน้าต่างปกติด้วย X เพื่อปิด Form2


person Whitehairedgeezer    schedule 06.12.2013    source แหล่งที่มา
comment
ใส่สิ่งที่ใช้เวลานานไว้ในเธรด   -  person David Heffernan    schedule 06.12.2013


คำตอบ (2)


เหตุการณ์นี้เกิดขึ้นเนื่องจากเธรดหลักไม่ว่าง และไม่สามารถประมวลผลข้อความในหน้าต่างได้

คุณควรย้ายการประมวลผลจำนวนมากในเธรด และใช้การซิงโครไนซ์เพื่อควบคุม

แฮ็คที่น่าเกลียดจะโทรมา

application.processmessages;

ในระหว่างการประมวลผลอย่างหนักเพื่อบังคับให้ประมวลผลข้อความของฟอร์มเมื่อฟอร์มหลักไม่ว่าง

คุณควรหาตัวอย่างการใช้งานเธรดและดูดีกว่า

person Ghigo    schedule 06.12.2013
comment
โปรดอย่าแนะนำ application.processmessages - person David Heffernan; 06.12.2013
comment
TApplication.ProcessMessages ไม่ใช่แฮ็ค - person Free Consulting; 06.12.2013
comment
มันไม่ใช่แฮ็คจริงๆ มันเป็นแค่การเขียนโค้ดที่ไม่ดี นั่นเป็นเหตุผลที่ฉันพูดถึงแฮ็คที่น่าเกลียด - person Ghigo; 06.12.2013
comment
เมื่อพิจารณาบริบทของคำถาม คุณยอมรับว่าการใช้ application.ProcessMessages ในกรณีนี้คือการเขียนโค้ดที่ไม่ถูกต้อง นี่ไม่ได้หมายความว่าการเข้ารหัสจะแย่เสมอไป - person Ghigo; 06.12.2013
comment
application.processmessages ใช้งานได้ - ปุ่มไม่ขยับ แต่เมื่อฉันส่งข้อความแต่ละข้อความจาก mainform มันจะพบเหตุการณ์ onclick ทำไมการเข้ารหัสจึงน่าเกลียดหรือแย่? - person Whitehairedgeezer; 06.12.2013
comment
@ user3074340: หากคุณไม่เข้าใจผลที่ตามมาของการเรียก Application.ProcessMessages คุณอาจพบผลข้างเคียงที่น่ารังเกียจตามมา ... ดังที่ David ชี้ให้เห็นเป็นความคิดเห็นภายใต้คำถามของคุณ คำตอบที่ถูกต้องคือการคำนวณแบบยาว ด้ายแยก... - person whosrdaddy; 06.12.2013

เนื่องจากฉันไม่สนับสนุนการใช้ Application.ProcessMessages ฉันจึงจะแสดงทางเลือกอื่นให้กับคุณด้วยการใช้เธรด ในตัวอย่างนี้ ฉันใช้ AsyncCalls ไลบรารีเธรด (สร้างโดย Andreas Hausladen) เพราะฉันชอบความเรียบง่าย ไลบรารีที่ยอดเยี่ยมอีกอันคือ OmniThreadLibrary ที่สร้างโดย SO สมาชิก Primož Gabrijelčič แต่ใช้ได้เฉพาะกับ Delphi เวอร์ชัน 2007 ขึ้นไปเท่านั้น

ตัวอย่างประกอบด้วย 2 แบบฟอร์ม แบบฟอร์มหลักที่มีปุ่ม Calculate และกล่องโต้ตอบความคืบหน้าที่แสดงแถบความคืบหน้าและปุ่ม Cancel โค้ดถูกสร้างขึ้นในลักษณะที่คุณสามารถใช้กล่องโต้ตอบความคืบหน้าซ้ำสำหรับการคำนวณอื่นๆ ได้ เนื่องจากไม่มีการขึ้นต่อกันแบบฮาร์ดโค้ด

รหัส .dpr:

program SO20424238;

uses
  Forms,
  u_frm_main in 'u_frm_main.pas' {Frm_main},
  u_dlg_progress in 'u_dlg_progress.pas' {ProgressDialog};

{$R *.res}

begin
  Application.Initialize;
  Application.MainFormOnTaskbar := True;
  Application.CreateForm(TFrm_main, Frm_main);
  Application.Run;
end.

แบบฟอร์มหลัก :

unit u_frm_main;

interface

uses
  u_dlg_progress,
  AsyncCalls,
  Windows,
  Messages,
  SysUtils,
  Classes,
  Controls,
  Forms, StdCtrls;

const
  INT_MAX_CALCULATIONS = 100;

type
  TFrm_main = class(TForm)
    Btn_docalculate: TButton;
    procedure Btn_docalculateClick(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
  private
    { Private declarations }
    CancelCalculation : Boolean;
    function SomeLongCalculation(OnProgress : TProgressEvent) : Integer;
    function ShowProgressDialog : TProgressDialog;
    procedure DoCalculate;
    procedure CancelEvent;
  public
    { Public declarations }
    Async : IAsyncCall;
  end;

var
  Frm_main: TFrm_main;

implementation

{$R *.dfm}

procedure TFrm_main.CancelEvent;
begin
 // set cancelation flag
 CancelCalculation := True;
end;

procedure TFrm_main.Btn_docalculateClick(Sender: TObject);
begin
 DoCalculate;
end;

function TFrm_main.ShowProgressDialog: TProgressDialog;
begin
 Result := TProgressDialog.Create(CancelEvent);
 Result.ProgressBar1.Max := INT_MAX_CALCULATIONS;
end;

function TFrm_main.SomeLongCalculation(OnProgress : TProgressEvent) : Integer;

var
  Index : Integer;

begin
 // BEWARE - this function runs in a different thread
 // *any* call to the VCL/GUI/shared variables must happen in the main (GUI) thread 
 // AsyncCalls make this easy by providing the EnterMainThread and LeaveMainThread functions
 for Index := 0 to INT_MAX_CALCULATIONS do
  begin
   Sleep(100); // replace this line with the actual calculation
   // now check if the user has canceled, check this in the main thread
   EnterMainThread;
   try
    if CancelCalculation then
     begin
      // notify progress window we are done
      if Assigned(OnProgress) then
       OnProgress(INT_MAX_CALCULATIONS);
      // exit calculation loop
      Break;
     end
    else
    // report actual progress
    if Assigned(OnProgress) then
     OnProgress(Index);
   finally
    LeaveMainThread;
   end;
  end;
end;

procedure TFrm_main.DoCalculate;

var
  ProgressDialog : TProgressDialog;

begin
 // create our progress dialog
 ProgressDialog := ShowProgressDialog;
 // reset cancelation flag
 CancelCalculation := False;
 // fire up calculation on a separate thread and hook up OnProgress function of our Progress dialog
 Async := TAsyncCalls.Invoke<TProgressEvent>(SomeLongCalculation, ProgressDialog.OnProgress);
 // show progress dialog, this will block all other forms from user input
 ProgressDialog.ShowModal;
end;

procedure TFrm_main.FormDestroy(Sender: TObject);
begin
 if Assigned(Async) then
  Async.Forget;
end;

end.

แบบฟอร์มหลัก dfm:

object Frm_main: TFrm_main
  Left = 0
  Top = 0
  Caption = 'Threading example'
  ClientHeight = 82
  ClientWidth = 273
  Color = clBtnFace
  Font.Charset = DEFAULT_CHARSET
  Font.Color = clWindowText
  Font.Height = -11
  Font.Name = 'Tahoma'
  Font.Style = []
  OldCreateOrder = False
  OnDestroy = FormDestroy
  PixelsPerInch = 96
  TextHeight = 13
  object Btn_docalculate: TButton
    Left = 92
    Top = 28
    Width = 75
    Height = 25
    Caption = 'Calculate!'
    TabOrder = 0
    OnClick = Btn_docalculateClick
  end
end

กล่องโต้ตอบความคืบหน้า:

unit u_dlg_progress;

interface

uses
  AsyncCalls,
  SysUtils,
  Controls,
  Forms,
  Dialogs,
  StdCtrls,
  ComCtrls,
  Classes;

type
  TCancelEvent = procedure of object;

  TProgressEvent = procedure(Value : Integer) of object;

  TProgressDialog = class(TForm)
    ProgressBar1: TProgressBar;
    Btn_cancel: TButton;
    Label1: TLabel;
    procedure FormCreate(Sender: TObject);
    procedure FormClose(Sender: TObject; var Action: TCloseAction);
    procedure Btn_cancelClick(Sender: TObject);
  private
    { Private declarations }
    FCancelEvent : TCancelEvent;
  public
    { Public declarations }
    procedure OnProgress(Value : Integer);
    constructor Create(CancelEvent : TCancelEvent);
  end;

implementation

{$R *.dfm}

{ TProgressDialog }

procedure TProgressDialog.Btn_cancelClick(Sender: TObject);
begin
 if Assigned(FCancelEvent) then
  FCancelEvent;
end;

procedure TProgressDialog.FormClose(Sender: TObject; var Action: TCloseAction);
begin
 // make sure our dialog is freed after use
 Action := caFree;
end;

procedure TProgressDialog.FormCreate(Sender: TObject);
begin
 // reset progress bar
 ProgressBar1.Position := 0;
end;

procedure TProgressDialog.OnProgress(Value: Integer);
begin
 if Value >= ProgressBar1.Max then
  Close;
 ProgressBar1.Position := Value;
 Label1.Caption := IntToStr(Value);
end;

constructor TProgressDialog.Create(CancelEvent: TCancelEvent);
begin
 inherited Create(nil);
 FCancelEvent := CancelEvent;
end;

end.

กล่องโต้ตอบความคืบหน้า dfm:

object ProgressDialog: TProgressDialog
  Left = 0
  Top = 0
  BorderIcons = []
  BorderStyle = bsDialog
  Caption = 'ProgressDialog'
  ClientHeight = 101
  ClientWidth = 364
  Color = clBtnFace
  Font.Charset = DEFAULT_CHARSET
  Font.Color = clWindowText
  Font.Height = -11
  Font.Name = 'Tahoma'
  Font.Style = []
  OldCreateOrder = False
  Position = poScreenCenter
  OnClose = FormClose
  OnCreate = FormCreate
  PixelsPerInch = 96
  TextHeight = 13
  object Label1: TLabel
    Left = 18
    Top = 55
    Width = 77
    Height = 26
    Caption = 'Label1'
  end
  object ProgressBar1: TProgressBar
    Left = 8
    Top = 16
    Width = 341
    Height = 25
    Smooth = True
    MarqueeInterval = 1
    Step = 1
    TabOrder = 0
  end
  object Btn_cancel: TButton
    Left = 136
    Top = 59
    Width = 75
    Height = 25
    Cancel = True
    Caption = '&Cancel'
    TabOrder = 1
    OnClick = Btn_cancelClick
  end
end
person whosrdaddy    schedule 06.12.2013