Control Windows app (HMA VPN) using PyWinAuto
Question:
I am trying to write Python code to request my vpn software to connect / disconnect upon certain conditions. I use HMA VPN and am running on Windows 10. Python 3.9.5.
The PyWinAuto module would appear to have the required functionality, but I cannot get it to work correctly. I’m sure it’s my coding, not the module itself… 😉
I’m fairly sure I’m referring to the control incorrectly when I call it with the click() method, but can anyone suggest the correct approach, given the dump_tree that I have included? (I have tried tens of variations based based on the code snippets in the Getting Started guide, but no joy).
I’m looking to be able to toggle Button4 in GroupBox2, which is named either "Connect" or "Disconnect", depending on the current state of the VPN. I also want to be able to click Button5, "Change IP Address", but once I figure out one button, the other should be easy.
Any thoughts where I am going wrong?
My code snippet is as follows:
from pywinauto import Desktop, Application
Desktop(backend='uia').HMAVPN.dump_tree()
app = Application().start('C:Program FilesPrivaxHMA VPNVpn.exe', timeout=5)
dlg_spec = app.window(title='HMA VPN')
dlg_spec.AvastChromiumWindow.Pane2.Document.GroupBox2.Button4.click()
The resulting output is as follows:
Control Identifiers:
Dialog - 'HMA VPN' (L-149, T68, R551, B638)
['HMA VPNDialog', 'HMA VPN', 'Dialog']
child_window(title="HMA VPN", control_type="Window")
|
| Pane - 'AvastChromiumWindow' (L-149, T68, R551, B638)
| ['Pane', 'AvastChromiumWindowPane', 'AvastChromiumWindow', 'Pane0', 'Pane1']
| child_window(title="AvastChromiumWindow", control_type="Pane")
| |
| | Pane - '' (L-149, T68, R551, B638)
| | ['Pane2', 'ue01bPane', 'ue01bPane0', 'ue01bPane1']
| | |
| | | Document - '   Original IP  91.90.253.244 New IP  80.255.5.198  Original IP  91.90.253.244  Location Germany, Berlin  ' (L-149, T68, R551, B638)
| | | ['ue01b ue014 ue007 Original IP xa0 91.90.253.244 New IP xa0 80.255.5.198 ue019 Original IP xa0 91.90.253.244 xa0 Location Germany, Berlin ue005 Document', 'Document', 'ue01b ue014 ue007 Original IP xa0 91.90.253.244 New IP xa0 80.255.5.198 ue019 Original IP xa0 91.90.253.244 xa0 Location Germany, Berlin ue005 ']
| | | child_window(title="   Original IP  91.90.253.244 New IP  80.255.5.198  Original IP  91.90.253.244  Location Germany, Berlin  ", auto_id="7211296", control_type="Document")
| | | |
| | | | GroupBox - '' (L-149, T68, R551, B104)
| | | | ['ue01bGroupBox', 'GroupBox', 'ue01bGroupBox0', 'ue01bGroupBox1', 'GroupBox0', 'GroupBox1']
| | | | |
| | | | | Button - 'Preferences' (L-134, T76, R-114, B96)
| | | | | ['PreferencesButton', 'Preferences', 'Button', 'Button0', 'Button1']
| | | | | child_window(title="Preferences", auto_id="hma_title__preferences", control_type="Button")
| | | | | |
| | | | | | Static - '' (L-134, T76, R-114, B96)
| | | | | | ['Static', 'ue01b', 'ue01bStatic', 'Static0', 'Static1']
| | | | | | child_window(title="", control_type="Text")
| | | | |
| | | | | Button - 'Minimize' (L486, T76, R506, B96)
| | | | | ['Minimize', 'Button2', 'MinimizeButton']
| | | | | child_window(title="Minimize", control_type="Button")
| | | | | |
| | | | | | Static - '' (L486, T76, R506, B96)
| | | | | | ['ue014Static', 'Static2', 'ue014']
| | | | | | child_window(title="", control_type="Text")
| | | | |
| | | | | Button - 'Close' (L516, T76, R536, B96)
| | | | | ['Close', 'Button3', 'CloseButton']
| | | | | child_window(title="Close", control_type="Button")
| | | | | |
| | | | | | Static - '' (L516, T76, R536, B96)
| | | | | | ['Static3', 'ue007', 'ue007Static']
| | | | | | child_window(title="", control_type="Text")
| | | |
| | | | GroupBox - '' (L-149, T68, R551, B638)
| | | | ['ue01bGroupBox2', 'GroupBox2']
| | | | |
| | | | | Image - '' (L111, T120, R291, B300)
| | | | | ['ue01bImage', 'Image']
| | | | |
| | | | | Button - 'Disconnect' (L126, T323, R276, B383)
| | | | | ['Button4', 'DisconnectButton', 'Disconnect']
| | | | | child_window(title="Disconnect", auto_id="dashboard_switch", control_type="Button")
| | | | |
| | | | | Static - 'Original IP' (L68, T414, R135, B430)
| | | | | ['Original IPStatic', 'Static4', 'Original IP', 'Original IPStatic0', 'Original IPStatic1', 'Original IP0', 'Original IP1']
| | | | | child_window(title="Original IP", control_type="Text")
| | | | |
| | | | | Static - 'Â ' (L164, T412, R168, B427)
| | | | | ['Static5', 'xa0Static', 'xa0', 'xa0Static0', 'xa0Static1', 'xa00', 'xa01']
| | | | | child_window(title="Â ", control_type="Text")
| | | | |
| | | | | Static - '91.90.253.244' (L184, T414, R266, B429)
| | | | | ['Static6', '91.90.253.244Static', '91.90.253.244', '91.90.253.244Static0', '91.90.253.244Static1', '91.90.253.2440', '91.90.253.2441']
| | | | | child_window(title="91.90.253.244", control_type="Text")
| | | | |
| | | | | Static - 'New IP' (L68, T442, R113, B458)
| | | | | ['New IP', 'Static7', 'New IPStatic']
| | | | | child_window(title="New IP", control_type="Text")
| | | | |
| | | | | Static - 'Â ' (L164, T440, R168, B455)
| | | | | ['Static8', 'xa0Static2', 'xa02']
| | | | | child_window(title="Â ", control_type="Text")
| | | | |
| | | | | Static - '80.255.5.198' (L184, T442, R258, B457)
| | | | | ['Static9', '80.255.5.198', '80.255.5.198Static']
| | | | | child_window(title="80.255.5.198", control_type="Text")
| | | | |
| | | | | Button - 'Change IP Address' (L294, T416, R334, B456)
| | | | | ['Change IP AddressButton', 'Button5', 'Change IP Address']
| | | | | child_window(title="Change IP Address", auto_id="ip_change", control_type="Button")
| | | | |
| | | | | Static - 'Original IP' (L95, T428, R162, B444)
| | | | | ['Original IPStatic2', 'Static10', 'Original IP2']
| | | | | child_window(title="Original IP", control_type="Text")
| | | | |
| | | | | Static - 'Â ' (L191, T426, R196, B441)
| | | | | ['Static11', 'xa0Static3', 'xa03']
| | | | | child_window(title="Â ", control_type="Text")
| | | | |
| | | | | Static - '91.90.253.244' (L211, T428, R293, B443)
| | | | | ['Static12', '91.90.253.244Static2', '91.90.253.2442']
| | | | | child_window(title="91.90.253.244", control_type="Text")
| | | | |
| | | | | Button - 'Location Germany, Berlin' (L31, T489, R371, B569)
| | | | | ['Button6', 'Location Germany, BerlinButton', 'Location Germany, Berlin']
| | | | | child_window(title="Location Germany, Berlin", auto_id="dashboard_chooseLocation", control_type="Button")
| | | | | |
| | | | | | Static - 'Â ' (L51, T507, R55, B522)
| | | | | | ['Static13', 'xa0Static4', 'xa04']
| | | | | | child_window(title="Â ", control_type="Text")
| | | | | |
| | | | | | Static - 'Location' (L111, T511, R164, B526)
| | | | | | ['Static14', 'Location', 'LocationStatic']
| | | | | | child_window(title="Location", control_type="Text")
| | | | | |
| | | | | | Static - 'Germany, Berlin' (L111, T529, R219, B546)
| | | | | | ['Static15', 'Germany, Berlin', 'Germany, BerlinStatic']
| | | | | | child_window(title="Germany, Berlin", control_type="Text")
| | | | | |
| | | | | | Static - '' (L331, T519, R351, B539)
| | | | | | ['Static16', 'ue005', 'ue005Static']
| | | | | | child_window(title="", control_type="Text")
| | | | |
| | | | | Button - 'More' (L420, T107, R528, B137)
| | | | | ['More', 'Button7', 'MoreButton']
| | | | | child_window(title="More", control_type="Button")
| | |
| | | Pane - '' (L-149, T68, R551, B638)
| | | ['Pane3', 'ue01bPane2']
Traceback (most recent call last):
File "C:Usersdb533VirtualBox VMspycharm-gunabotvenvlibsite-packagespywinautoapplication.py", line 250, in __resolve_control
ctrl = wait_until_passes(
File "C:Usersdb533VirtualBox VMspycharm-gunabotvenvlibsite-packagespywinautotimings.py", line 458, in wait_until_passes
raise err
pywinauto.timings.TimeoutError
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "C:Usersdb533PycharmProjectsGunaBothma_control.py", line 19, in <module>
dlg_spec.AvastChromiumWindow.Pane2.Document.GroupBox2.Button4.click()
File "C:Usersdb533VirtualBox VMspycharm-gunabotvenvlibsite-packagespywinautoapplication.py", line 379, in __getattribute__
ctrls = self.__resolve_control(self.criteria)
File "C:Usersdb533VirtualBox VMspycharm-gunabotvenvlibsite-packagespywinautoapplication.py", line 261, in __resolve_control
raise e.original_exception
File "C:Usersdb533VirtualBox VMspycharm-gunabotvenvlibsite-packagespywinautotimings.py", line 436, in wait_until_passes
func_val = func(*args, **kwargs)
File "C:Usersdb533VirtualBox VMspycharm-gunabotvenvlibsite-packagespywinautoapplication.py", line 203, in __get_ctrl
dialog = self.backend.generic_wrapper_class(findwindows.find_element(**criteria[0]))
File "C:Usersdb533VirtualBox VMspycharm-gunabotvenvlibsite-packagespywinautofindwindows.py", line 87, in find_element
raise ElementNotFoundError(kwargs)
pywinauto.findwindows.ElementNotFoundError: {'title': 'HMA VPN', 'backend': 'win32', 'process': 40580}
Process finished with exit code 1
Answers:
Cracked it! The following code let’s me toggle the Connect / Disconnect button and change the IP.
from pywinauto import Desktop,Application
vpn_app = Application(backend="uia").start('C:Program FilesPrivaxHMA VPNVpn.exe')
dialog=Desktop(backend="uia").HMA
panel0=dialog.Pane
# Command to connect / disconnect the VPN: connect_button.click()
connect_button=panel0.ConnectButton
# Command to change the IP address: changeIP.click()
changeIP=panel0.Button5
# Check VPN state:
# 0 if disconnected
# 1 if connected
print(connect_button.get_toggle_state())
# Command to connect / disconnect the VPN: connect_button.click()
connect_button=panel0.ConnectButton
connect_button.click()
# Command to change the IP address: changeIP.click()
changeIP=panel0.Button5
changeIP.click()
# Check VPN state:
# 0 if disconnected
# 1 if connected
print(connect_button.get_toggle_state())
I am trying to write Python code to request my vpn software to connect / disconnect upon certain conditions. I use HMA VPN and am running on Windows 10. Python 3.9.5.
The PyWinAuto module would appear to have the required functionality, but I cannot get it to work correctly. I’m sure it’s my coding, not the module itself… 😉
I’m fairly sure I’m referring to the control incorrectly when I call it with the click() method, but can anyone suggest the correct approach, given the dump_tree that I have included? (I have tried tens of variations based based on the code snippets in the Getting Started guide, but no joy).
I’m looking to be able to toggle Button4 in GroupBox2, which is named either "Connect" or "Disconnect", depending on the current state of the VPN. I also want to be able to click Button5, "Change IP Address", but once I figure out one button, the other should be easy.
Any thoughts where I am going wrong?
My code snippet is as follows:
from pywinauto import Desktop, Application
Desktop(backend='uia').HMAVPN.dump_tree()
app = Application().start('C:Program FilesPrivaxHMA VPNVpn.exe', timeout=5)
dlg_spec = app.window(title='HMA VPN')
dlg_spec.AvastChromiumWindow.Pane2.Document.GroupBox2.Button4.click()
The resulting output is as follows:
Control Identifiers:
Dialog - 'HMA VPN' (L-149, T68, R551, B638)
['HMA VPNDialog', 'HMA VPN', 'Dialog']
child_window(title="HMA VPN", control_type="Window")
|
| Pane - 'AvastChromiumWindow' (L-149, T68, R551, B638)
| ['Pane', 'AvastChromiumWindowPane', 'AvastChromiumWindow', 'Pane0', 'Pane1']
| child_window(title="AvastChromiumWindow", control_type="Pane")
| |
| | Pane - '' (L-149, T68, R551, B638)
| | ['Pane2', 'ue01bPane', 'ue01bPane0', 'ue01bPane1']
| | |
| | | Document - '   Original IP  91.90.253.244 New IP  80.255.5.198  Original IP  91.90.253.244  Location Germany, Berlin  ' (L-149, T68, R551, B638)
| | | ['ue01b ue014 ue007 Original IP xa0 91.90.253.244 New IP xa0 80.255.5.198 ue019 Original IP xa0 91.90.253.244 xa0 Location Germany, Berlin ue005 Document', 'Document', 'ue01b ue014 ue007 Original IP xa0 91.90.253.244 New IP xa0 80.255.5.198 ue019 Original IP xa0 91.90.253.244 xa0 Location Germany, Berlin ue005 ']
| | | child_window(title="   Original IP  91.90.253.244 New IP  80.255.5.198  Original IP  91.90.253.244  Location Germany, Berlin  ", auto_id="7211296", control_type="Document")
| | | |
| | | | GroupBox - '' (L-149, T68, R551, B104)
| | | | ['ue01bGroupBox', 'GroupBox', 'ue01bGroupBox0', 'ue01bGroupBox1', 'GroupBox0', 'GroupBox1']
| | | | |
| | | | | Button - 'Preferences' (L-134, T76, R-114, B96)
| | | | | ['PreferencesButton', 'Preferences', 'Button', 'Button0', 'Button1']
| | | | | child_window(title="Preferences", auto_id="hma_title__preferences", control_type="Button")
| | | | | |
| | | | | | Static - '' (L-134, T76, R-114, B96)
| | | | | | ['Static', 'ue01b', 'ue01bStatic', 'Static0', 'Static1']
| | | | | | child_window(title="", control_type="Text")
| | | | |
| | | | | Button - 'Minimize' (L486, T76, R506, B96)
| | | | | ['Minimize', 'Button2', 'MinimizeButton']
| | | | | child_window(title="Minimize", control_type="Button")
| | | | | |
| | | | | | Static - '' (L486, T76, R506, B96)
| | | | | | ['ue014Static', 'Static2', 'ue014']
| | | | | | child_window(title="", control_type="Text")
| | | | |
| | | | | Button - 'Close' (L516, T76, R536, B96)
| | | | | ['Close', 'Button3', 'CloseButton']
| | | | | child_window(title="Close", control_type="Button")
| | | | | |
| | | | | | Static - '' (L516, T76, R536, B96)
| | | | | | ['Static3', 'ue007', 'ue007Static']
| | | | | | child_window(title="", control_type="Text")
| | | |
| | | | GroupBox - '' (L-149, T68, R551, B638)
| | | | ['ue01bGroupBox2', 'GroupBox2']
| | | | |
| | | | | Image - '' (L111, T120, R291, B300)
| | | | | ['ue01bImage', 'Image']
| | | | |
| | | | | Button - 'Disconnect' (L126, T323, R276, B383)
| | | | | ['Button4', 'DisconnectButton', 'Disconnect']
| | | | | child_window(title="Disconnect", auto_id="dashboard_switch", control_type="Button")
| | | | |
| | | | | Static - 'Original IP' (L68, T414, R135, B430)
| | | | | ['Original IPStatic', 'Static4', 'Original IP', 'Original IPStatic0', 'Original IPStatic1', 'Original IP0', 'Original IP1']
| | | | | child_window(title="Original IP", control_type="Text")
| | | | |
| | | | | Static - 'Â ' (L164, T412, R168, B427)
| | | | | ['Static5', 'xa0Static', 'xa0', 'xa0Static0', 'xa0Static1', 'xa00', 'xa01']
| | | | | child_window(title="Â ", control_type="Text")
| | | | |
| | | | | Static - '91.90.253.244' (L184, T414, R266, B429)
| | | | | ['Static6', '91.90.253.244Static', '91.90.253.244', '91.90.253.244Static0', '91.90.253.244Static1', '91.90.253.2440', '91.90.253.2441']
| | | | | child_window(title="91.90.253.244", control_type="Text")
| | | | |
| | | | | Static - 'New IP' (L68, T442, R113, B458)
| | | | | ['New IP', 'Static7', 'New IPStatic']
| | | | | child_window(title="New IP", control_type="Text")
| | | | |
| | | | | Static - 'Â ' (L164, T440, R168, B455)
| | | | | ['Static8', 'xa0Static2', 'xa02']
| | | | | child_window(title="Â ", control_type="Text")
| | | | |
| | | | | Static - '80.255.5.198' (L184, T442, R258, B457)
| | | | | ['Static9', '80.255.5.198', '80.255.5.198Static']
| | | | | child_window(title="80.255.5.198", control_type="Text")
| | | | |
| | | | | Button - 'Change IP Address' (L294, T416, R334, B456)
| | | | | ['Change IP AddressButton', 'Button5', 'Change IP Address']
| | | | | child_window(title="Change IP Address", auto_id="ip_change", control_type="Button")
| | | | |
| | | | | Static - 'Original IP' (L95, T428, R162, B444)
| | | | | ['Original IPStatic2', 'Static10', 'Original IP2']
| | | | | child_window(title="Original IP", control_type="Text")
| | | | |
| | | | | Static - 'Â ' (L191, T426, R196, B441)
| | | | | ['Static11', 'xa0Static3', 'xa03']
| | | | | child_window(title="Â ", control_type="Text")
| | | | |
| | | | | Static - '91.90.253.244' (L211, T428, R293, B443)
| | | | | ['Static12', '91.90.253.244Static2', '91.90.253.2442']
| | | | | child_window(title="91.90.253.244", control_type="Text")
| | | | |
| | | | | Button - 'Location Germany, Berlin' (L31, T489, R371, B569)
| | | | | ['Button6', 'Location Germany, BerlinButton', 'Location Germany, Berlin']
| | | | | child_window(title="Location Germany, Berlin", auto_id="dashboard_chooseLocation", control_type="Button")
| | | | | |
| | | | | | Static - 'Â ' (L51, T507, R55, B522)
| | | | | | ['Static13', 'xa0Static4', 'xa04']
| | | | | | child_window(title="Â ", control_type="Text")
| | | | | |
| | | | | | Static - 'Location' (L111, T511, R164, B526)
| | | | | | ['Static14', 'Location', 'LocationStatic']
| | | | | | child_window(title="Location", control_type="Text")
| | | | | |
| | | | | | Static - 'Germany, Berlin' (L111, T529, R219, B546)
| | | | | | ['Static15', 'Germany, Berlin', 'Germany, BerlinStatic']
| | | | | | child_window(title="Germany, Berlin", control_type="Text")
| | | | | |
| | | | | | Static - '' (L331, T519, R351, B539)
| | | | | | ['Static16', 'ue005', 'ue005Static']
| | | | | | child_window(title="", control_type="Text")
| | | | |
| | | | | Button - 'More' (L420, T107, R528, B137)
| | | | | ['More', 'Button7', 'MoreButton']
| | | | | child_window(title="More", control_type="Button")
| | |
| | | Pane - '' (L-149, T68, R551, B638)
| | | ['Pane3', 'ue01bPane2']
Traceback (most recent call last):
File "C:Usersdb533VirtualBox VMspycharm-gunabotvenvlibsite-packagespywinautoapplication.py", line 250, in __resolve_control
ctrl = wait_until_passes(
File "C:Usersdb533VirtualBox VMspycharm-gunabotvenvlibsite-packagespywinautotimings.py", line 458, in wait_until_passes
raise err
pywinauto.timings.TimeoutError
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "C:Usersdb533PycharmProjectsGunaBothma_control.py", line 19, in <module>
dlg_spec.AvastChromiumWindow.Pane2.Document.GroupBox2.Button4.click()
File "C:Usersdb533VirtualBox VMspycharm-gunabotvenvlibsite-packagespywinautoapplication.py", line 379, in __getattribute__
ctrls = self.__resolve_control(self.criteria)
File "C:Usersdb533VirtualBox VMspycharm-gunabotvenvlibsite-packagespywinautoapplication.py", line 261, in __resolve_control
raise e.original_exception
File "C:Usersdb533VirtualBox VMspycharm-gunabotvenvlibsite-packagespywinautotimings.py", line 436, in wait_until_passes
func_val = func(*args, **kwargs)
File "C:Usersdb533VirtualBox VMspycharm-gunabotvenvlibsite-packagespywinautoapplication.py", line 203, in __get_ctrl
dialog = self.backend.generic_wrapper_class(findwindows.find_element(**criteria[0]))
File "C:Usersdb533VirtualBox VMspycharm-gunabotvenvlibsite-packagespywinautofindwindows.py", line 87, in find_element
raise ElementNotFoundError(kwargs)
pywinauto.findwindows.ElementNotFoundError: {'title': 'HMA VPN', 'backend': 'win32', 'process': 40580}
Process finished with exit code 1
Cracked it! The following code let’s me toggle the Connect / Disconnect button and change the IP.
from pywinauto import Desktop,Application
vpn_app = Application(backend="uia").start('C:Program FilesPrivaxHMA VPNVpn.exe')
dialog=Desktop(backend="uia").HMA
panel0=dialog.Pane
# Command to connect / disconnect the VPN: connect_button.click()
connect_button=panel0.ConnectButton
# Command to change the IP address: changeIP.click()
changeIP=panel0.Button5
# Check VPN state:
# 0 if disconnected
# 1 if connected
print(connect_button.get_toggle_state())
# Command to connect / disconnect the VPN: connect_button.click()
connect_button=panel0.ConnectButton
connect_button.click()
# Command to change the IP address: changeIP.click()
changeIP=panel0.Button5
changeIP.click()
# Check VPN state:
# 0 if disconnected
# 1 if connected
print(connect_button.get_toggle_state())