[冗长的帖子,道歉]
我正在寻找有关CL_GUI_TIMER
的确认:(program group的每个internal 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.
在此执行顺序中:
Z_MAIN
调用其屏幕200
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注释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( ).
答案 0 :(得分:3)
在SAP注释2679117 - CL_GUI_TIMER: Timer is aborted when a dialog is opened in SAP GUI中对此进行了解释:
CL_GUI_TIMER不能在计时器运行时处理切换屏幕。
据我了解,SAP说他们不再支持此类。
因此,这意味着“不确定的行为,使用后果自负”。
对不起!
答案 1 :(得分:1)
在上述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=>screen0
)上的事件是不相关或必须被抑制的。
Thomas Jung拥有一个古老的blog post,这为CFW事件的内部运作提供了一些启示。另外,CL_GUI_CFW中的“ stackpos”似乎很重要。