My application (script) is a robot that automates certain user actions in SAPGUI .
Due to the fact that some operations on the part of SAP are delayed for minutes, I have to create several sessions inside SAPGUI to save time. Each session in SAPGUI is one window (with one handle) inside which the forms are replaced. In the process of work, dialog boxes belonging to this window are called.
Threads are synchronized via threading.RLock()
. This is basically a simple classic multi-stream. Naturally, every time you enter the acquire()/release()
block, you have to switch the focus to the required session window, which the set_focus()
method basically copes with until it comes to dialog boxes.
The following multi-thread wait function is used:
def wait_dialog(self, name, timeout=SAP_OPERATION_TIMEOUT): ActionLogger.log(f'Wait for "{name}"') def check_state(window): with lock: new_window = window.window(title_re=name, class_name=class_names.dialog) new_window.verify_actionable() return(new_window) new_window = pywinauto.timings.wait_until_passes(timeout, 0.1, lambda: check_state(self.window)) return(SapWindow(new_window))
where lock
is an instance of the RLock
class.
So in the next piece of code, set_focus()
makes the dialog active and supposedly focused ( is_active() == True
), but there is no focus on any element and type_keys()
cannot do anything and does not issue errors. While Button2.click()
works fine:
dlg4 = sess.wait_dialog(sess.name, timeout=SAPbot.SAP_SPOOL_TIMEOUT) with SAPbot.lock: dlg4.set_focus() dlg4.window.type_keys(filename + '{TAB 6}' + workDir) dlg4.window.Button2.click()
If you replace dlg4.set_focus()
with SetForegroundWindow(dlg4.window.wrapper_object().handle)
, then everything will work fine under Windows 10 , but not on Windows Server 2012 via RDP . Under server Windows, when executing by the third thread (why I’m not sure why during the third run) of this code, the SAPGUI icon flashes and the same story as with set_focus()
, only the window does not even appear. I understand that blinking means that the Windows cannot switch the focus because I supposedly work with another window, in fact there isn’t - access to the windows is atomic via RLock
, as I already wrote above.
But there is one interesting solution to LockSetForegroundWindow : if you use the following code for focusing, everything works correctly:
if not dlg4.window.is_active(): dlg4.window.type_keys('{VK_MENU}') SetForegroundWindow(dlg4.window.wrapper_object().handle)
SetForegroundWindow
is called inside.set_focus()
(this is probably understandable). But the feature of Python is that its threads are not executed in parallel, but in the time-sharing mode. Accordingly, context switches occur at arbitrary moments. If the action under the focus is not one, then the switch may occur in the middle and the focus will go to another window in another thread.LockSetForegroundWindow
can really solve part of the problem, but not 100% of the time. - Vasily Ryabovtype_keys()
andclick_input()
, so that focus is not required. For example, forbackend="win32"
there is a.send_keystrokes()
method that usesWM_KEYDOWN
andWM_KEYUP
, as well asWM_ACTIVATE
. It also supports keyboard shortcuts (although their work is not guaranteed for all applications), and even uses the active layout (you can try^N
on the Russian layout, by the way, it will suddenly work, although it is unlikely). - Vasily Ryabov.verify_actionable()
. - Vasily Ryabov