【问题标题】:What is the "scope" of CL_GUI_TIMER?CL_GUI_TIMER 的“范围”是什么?
【发布时间】:2023-03-17 09:41:01
【问题描述】:

[冗长的帖子,道歉]

我正在寻找关于CL_GUI_TIMER 的确认:必须每个program groupinternal session有自己的专用实例CL_GUI_TIMER 对象? 我发现如果对CL_GUI_TIMER 对象的引用与另一个程序组共享,那么从第二个程序组中调用run() 将不会触发finished 事件?

我会尽量总结一下相关代码:

有一个类实现了一个定时器(当它到期时离开主程序)。 注意:类下面的错误检查被剥离,但在实例化或方法调用期间实际代码中不会发生错误/异常:

CLASS zcl_cs_gui_timer_leave_prog DEFINITION.
  PUBLIC SECTION.
    METHODS constructor.
    METHODS set_timer.

  PROTECTED SECTION.
    DATA      go_gui_timer              TYPE REF TO cl_gui_timer .
    CONSTANTS c_default_timeout_seconds TYPE        i VALUE 6 ##NO_TEXT.

  PRIVATE SECTION.
    METHODS timer_handler FOR EVENT finished OF cl_gui_timer .
ENDCLASS.


CLASS zcl_cs_gui_timer_leave_prog IMPLEMENTATION.
  METHOD constructor.
    go_gui_timer = NEW #( ).
    SET HANDLER me->timer_handler FOR go_gui_timer.
    go_gui_timer->interval = c_default_timeout_seconds.
  ENDMETHOD.

  METHOD set_timer.
    go_gui_timer->cancel( ).
    go_gui_timer->run( ).
  ENDMETHOD.

  METHOD timer_handler.
    MESSAGE 'Transaction ended due to inactivity' TYPE 'S'.
    LEAVE PROGRAM.
  ENDMETHOD.
ENDCLASS.

函数组Z_FGROUP 有一个函数模块Z_CS_SET_GUI_TIMEOUT 具有上述类的静态实例(注意:如果将静态变量设为“全局”函数组,则行为是相同的 ):

FUNCTION z_cs_set_gui_timeout.

  STATICS lo_zcs_gui_timer TYPE REF TO zcl_cs_gui_timer_leave_prog.

  TRY.
      IF NOT lo_zcs_gui_timer IS BOUND.
        lo_zcs_gui_timer = NEW #( ).
      ENDIF.
      lo_zcs_gui_timer->set_timer( ).
    CATCH cx_abap_context_info_error INTO DATA(go_exc).
      MESSAGE |{ go_exc->get_longtext( ) }| TYPE 'S'.
  ENDTRY.
ENDFUNCTION.

函数组Z_FGROUP还有另一个函数模块Z_FGROUP_CALL_SCREEN调用屏幕100,定义在Z_FGROUP中。在该屏幕的 PBO 中,我们有模块

MODULE set_gui_timeout_100 OUTPUT.
* actually is in a subroutine (not the module), skipping for brevity:
  CALL FUNCTION 'Z_CS_SET_GUI_TIMEOUT'.
ENDMODULE.

现在一个主程序,比如Z_MAIN 也有自己的屏幕200,在其PBO 中对Z_CS_SET_GUI_TIMEOUT 进行相同的调用:

MODULE set_gui_timeout_200 OUTPUT.
* actually is in a subroutine (not the module), skipping for brevity:
  CALL FUNCTION 'Z_CS_SET_GUI_TIMEOUT'.
ENDMODULE.

在这个执行序列中:

  1. Z_MAIN 调用它的屏幕 200
    那么可能,
  2. Z_MAIN 调用功能模块Z_FGROUP_CALL_SCREEN(然后调用其屏幕100

第一步确实正确启动了计时器。如果您不执行第 2 步,则此第一个计时器将过期,从而导致 LEAVE PROGRAM 符合预期。 但是,如果您还执行第 2 步(显然在第 1 步计时器到期之前),将会发生的情况是第 2 步不会触发任何计时器事件。因此,您可以在屏幕上停留100,只要您愿意。同时,以#1 启动的计时器“静默”到期,即不会触发完成事件,当您在屏幕上时可以处理该事件100

如果在上面的代码中您将Z_MAIN 的屏幕200 计时器更改为调用它自己的CL_GUI_TIMER 实例(而不是函数组的实例):

MODULE set_gui_timeout_200 OUTPUT.
* actually is in a subroutine (not the module), skipping for brevity:

**  CALL FUNCTION 'Z_CS_SET_GUI_TIMEOUT'.

  STATICS lo_zcs_gui_timer TYPE REF TO zcl_cs_gui_timer_leave_prog.

  TRY.
      IF NOT lo_zcs_gui_timer IS BOUND.
        lo_zcs_gui_timer = NEW #( ).
      ENDIF.
      lo_zcs_gui_timer->set_timer( ).
    CATCH cx_abap_context_info_error INTO DATA(go_exc).
      MESSAGE |{ go_exc->get_longtext( ) }| TYPE 'S'.
  ENDTRY.
ENDMODULE.

然后一切正常,两个屏幕都有一个计时器(触发finished 事件)。


另外,在 Sandra 的回答指向 SAP note 2679117 之后。程序演示了一个底层计时器继续运行,如果模态对话框在到期前关闭,它将触发:

PROGRAM ztimer_event.


* +-------------------------------------------------------------------------------------------------+
PARAMETERS:
* +-------------------------------------------------------------------------------------------------+
  p_time TYPE i DEFAULT '6'.



* +-------------------------------------------------------------------------------------------------+
CLASS zcl_cs_gui_timer_leave_prog DEFINITION.
  PUBLIC SECTION.
    METHODS:
      constructor,
      set_timer.
    CONSTANTS:
      c_default_timeout_seconds TYPE i VALUE 6 ##NO_TEXT.
    DATA:
      lo_gui_timer              TYPE REF TO cl_gui_timer .

  PRIVATE SECTION.
    METHODS timer_handler FOR EVENT finished OF cl_gui_timer .
ENDCLASS.


CLASS zcl_cs_gui_timer_leave_prog IMPLEMENTATION.
  METHOD constructor.
    lo_gui_timer = NEW #( ).
    SET HANDLER me->timer_handler FOR lo_gui_timer.
    lo_gui_timer->interval = COND #( WHEN p_time <= 0 THEN c_default_timeout_seconds
                                     ELSE p_time ).
  ENDMETHOD.

  METHOD set_timer.
    lo_gui_timer->cancel( ).
    lo_gui_timer->run( ).
  ENDMETHOD.

  METHOD timer_handler.
    MESSAGE 'Timer was triggered' TYPE 'I'.
*    LEAVE PROGRAM.
  ENDMETHOD.
ENDCLASS.
* +-------------------------------------------------------------------------------------------------+


* +-------------------------------------------------------------------------------------------------+
START-OF-SELECTION.
* +-------------------------------------------------------------------------------------------------+
  TYPES:
    BEGIN OF t_alv_row,
      text TYPE string,
    END OF t_alv_row.

  DATA:
    lr_salv TYPE REF TO            cl_salv_table,
    lt_alv  TYPE STANDARD TABLE OF t_alv_row.


  DATA(go_timer) = NEW zcl_cs_gui_timer_leave_prog( ).
  go_timer->set_timer( ).

  lt_alv = VALUE #( ( text = |Timer is running. To let it expire silently:| )
                    ( text = |Wait { go_timer->lo_gui_timer->interval } |
                           & |seconds before closing this popup'| ) ).

  cl_salv_table=>factory(
    EXPORTING
       list_display = abap_false
    IMPORTING
      r_salv_table = lr_salv
    CHANGING
       t_table     = lt_alv ).

  lr_salv->set_screen_popup(
    start_column = 10
    end_column   = 60
    start_line   = 5
    end_line     = 9 ).
  WRITE: / |This will not time out if approximately { go_timer->lo_gui_timer->interval } seconds |
         & |passed before you closed the popup.|,
         / |It will timeout (at approximately { go_timer->lo_gui_timer->interval } |
         & |seconds from execution) if you closed the popup earlier|.

  lr_salv->display( ).

【问题讨论】:

    标签: abap sap-gui


    【解决方案1】:

    在 SAP 说明 2679117 - CL_GUI_TIMER: Timer is aborted when a dialog is opened in SAP GUI 中有说明:

    CL_GUI_TIMER 不是为在计时器运行时处理切换屏幕而设计的。

    据我了解,SAP 表示他们不再支持此类。

    所以,它的意思是“未定义的行为,使用它需要您自担风险”。

    对不起!

    【讨论】:

    • 为了完整起见,当注释说“在 SAP GUI 中打开模式对话框时,计时器被中止”时,注释并不完全准确。如果在底层计时器到期之前关闭模态对话框,则触发并处理该事件:
    • 在此问题中添加了示例程序
    【解决方案2】:

    按照上面 Sandra 的回答,SAP 注释提到“模态对话框”:

    事实证明,节目组与问题无关。为每个程序组创建一个新的CL_GUI_TIMER 实例恰好解决了这个问题,因为它只是为单独的 GUI 控件创建了一个新的系统事件绑定。参考我的示例代码,这个单独的控件对应于功能组的Z_FGROUP 屏幕100。此屏幕以不同的“popup level”(使用CALL SCREEN 0100 STARTING AT ...)调用。事实证明,这个被忽视的细节是关键:

    control framework 中,系统事件被注册:

    大概CL_GUI_TIMER 是通过GUI 控件实现的。但是每个 GUI 控件都绑定到一个弹出级别。如果控件需要“容器控件”,则容器也绑定到“弹出级别”。当 GUI 处于不同的弹出级别时,属于另一个级别的任何控件都是“不可见的”。

    所以,我猜,自动化控制器将为任何此类不可见(“超出当前弹出级别”)控件(容器)触发系统事件。但控制框架不会调用处理程序,因为弹出级别不是当前的。计时器保持运行,因此如果您及时回到正确的弹出级别,自动化控制器将触发事件并将其推送到控制框架(这解释了我问题后半部分的行为)。

    控制框架旨在处理用户交互,并且假设似乎是当有弹出窗口时,其他级别的事件(包括主窗口CL_GUI_CONTAINER=&gt;screen0)是不相关的或必须被抑制。

    Thomas Jung 有一个旧的blog post,它揭示了 CFW 事件的内部运作。此外,CL_GUI_CFW 中的“stackpos”似乎很相关。

    【讨论】:

      猜你喜欢
      • 2011-11-11
      • 2020-10-18
      • 2017-04-22
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多