Какова область действия CL_GUI_TIMER? - PullRequest
4 голосов
/ 08 июля 2019

[Длинный пост, извинения]

Я ищу подтверждение относительно CL_GUI_TIMER: Должна ли каждая группа программ (из внутреннего сеанса ) иметь свой собственный выделенный экземпляр объекта 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, с тем же вызовом Z_CS_SET_GUI_TIMEOUT в ее PBO:

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 timer для вызова своего собственного 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).


Кроме того, после ответа Сандры, указывающего на 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( ).

Ответы [ 2 ]

3 голосов
/ 08 июля 2019

Это объясняется в SAP-ноте 2679117 - CL_GUI_TIMER: таймер прерывается при открытии диалога в SAP GUI :

CL_GUI_TIMER не предназначен для обработки экранов переключения, покатаймер запущен.

Насколько я понимаю, это примечание, SAP говорит, что они больше не поддерживают этот класс.

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

Извините!

1 голос
/ 09 июля 2019

В ответ на ответ Сандры выше, где в примечании SAP упоминается «модальное диалоговое окно»:

Оказывается, группы программ не имеют отношения к вопросу.Создание нового экземпляра CL_GUI_TIMER для каждой группы программ помогло решить проблему, поскольку оно просто создало новую привязку системных событий для отдельного элемента управления GUI.Что касается моего примера кода, этот отдельный элемент управления соответствовал экрану Z_FGROUP группы функций 100.Этот экран вызывался с другим « всплывающим уровнем » (с CALL SCREEN 0100 STARTING AT ...).Оказывается, эта упущенная деталь является ключевой:

В рамках структуры управления регистрируются системные события: enter image description here

Предположительно CL_GUI_TIMER реализован с управлением графическим интерфейсом.Но каждый элемент управления GUI привязан к всплывающему уровню.Если элемент управления требует «управления контейнером», то контейнер также привязывается к «всплывающему уровню».Когда графический интерфейс пользователя находится на другом всплывающем уровне, любой элемент управления, принадлежащий другому уровню, является «невидимым».

Итак, я полагаю, контроллер автоматизации будет запускать системные события для любых таких невидимых («вне текущего уровня всплывающих окон») элементов управления (контейнеров).Но структура управления не будет вызывать обработчик, потому что уровень всплывающих окон не является текущим.Таймер продолжает работать, поэтому, если вы вернетесь к нужному всплывающему уровню во времени, контроллер автоматизации запустит событие и отправит его в структуру управления (это объясняет поведение в последней части моего вопроса).

Структура управления была разработана для обработки взаимодействия с пользователем, и, как представляется, предполагается, что при появлении всплывающего окна события на других уровнях (включая главное окно CL_GUI_CONTAINER=>screen0) не имеют значения или должны подавляться.

ТомасУ Юнга есть старая запись в блоге , которая проливает свет на внутреннюю работу событий CFW.Кроме того, «стэкпос» в CL_GUI_CFW представляется актуальным.

...