Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement native backend for WPF applications #1210

Draft
wants to merge 59 commits into
base: atspi
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
59 commits
Select commit Hold shift + click to select a range
376c4fe
Add initial WPF backend
eltimen Mar 13, 2022
43440a6
attempt to fix tests failure
eltimen Mar 13, 2022
93c050b
improve WpfElementInfo
eltimen Mar 25, 2022
8c0bcbf
add rectangle property
eltimen Mar 25, 2022
154ab50
minor change in WpfElement info
eltimen Mar 26, 2022
9d8df47
WPF backend: add pid to criteria in .by() method
eltimen Mar 27, 2022
abd3b84
add handle property, move pipe data to singleton
eltimen Apr 2, 2022
0aa3f99
parent property
eltimen Apr 5, 2022
66570c4
move injected part to the submodule
eltimen Apr 9, 2022
158abbe
improve injected module structure
eltimen Apr 12, 2022
f23604b
add tests for WPFElementInfo
eltimen Apr 12, 2022
b8e8eb3
remove injected dll building before test
eltimen Apr 13, 2022
df7d637
fix errors from ci
eltimen Apr 13, 2022
cf316a2
add tests for wpf wrapper, click_input works
eltimen Apr 13, 2022
abcbc1e
control_type works
eltimen Apr 14, 2022
202666c
fix best_match for wpf backend
eltimen Apr 14, 2022
1fe8f83
wrapper: focus-related methods, initial tests
eltimen Apr 14, 2022
2832566
make WPFWrapper tests green
eltimen Apr 16, 2022
aa9434a
use GetName action and update submodule
eltimen Apr 18, 2022
ab37703
add WindowWrapper
eltimen Apr 19, 2022
beb034e
add buttonwrapper
eltimen Apr 21, 2022
effd80f
add ComboBox wrapper
eltimen Apr 21, 2022
28bee81
fix ImportError on Python 2.7
eltimen Apr 23, 2022
9ee9629
update submodule
eltimen Apr 23, 2022
7d95358
add EditWrapper
eltimen Apr 25, 2022
4f6cfd4
add tab control wrapper
eltimen Apr 26, 2022
f7500a5
update submodule
eltimen Apr 26, 2022
6a34682
add slider and toolbar wrappers
eltimen Apr 26, 2022
9a68da3
add support of filter criteria in WpfElementInfo
eltimen Apr 28, 2022
ec49d24
add menu wrapper
eltimen Apr 28, 2022
8fec5b4
add treeview support
eltimen Apr 30, 2022
05435b2
update submodule
eltimen Apr 30, 2022
330bf3b
add listbox support
eltimen May 1, 2022
455adf2
remove extra code for adding pid to criteria
eltimen May 4, 2022
ef47033
fix code style
eltimen May 7, 2022
206e80a
Add support of ListView with a GridView view
eltimen May 9, 2022
68ae33b
update submodule
eltimen May 9, 2022
019dce7
add value property
eltimen May 9, 2022
1ac3f90
fix test and typo in DataGridWrapper
eltimen May 9, 2022
7c7118d
add DataGrid support
eltimen May 9, 2022
db836c2
update submodule
eltimen May 9, 2022
22966ce
fix getting text of ListView items
eltimen May 10, 2022
e9b7856
update submodule
eltimen May 10, 2022
e0a56b1
update submodule, minor change
eltimen May 10, 2022
4274d64
fix creation of WPFWrapper subclasses
eltimen May 11, 2022
6e48904
rename exceptions from injected submodule
eltimen May 11, 2022
e7a5ae2
fix docstrings for grid wrapper
eltimen May 12, 2022
50b7ec6
update submodule
eltimen May 12, 2022
0526c7a
fix children() test
eltimen May 12, 2022
89541c9
add method to get list of native properties
eltimen May 24, 2022
2622f90
update submodule
eltimen May 24, 2022
b9609a7
rename property-related methods in WPFWrapper
eltimen May 26, 2022
7ff48f6
fix issues from code analysis (mostly docstrings)
eltimen Jun 7, 2022
b1012a4
update submodule
eltimen Jun 19, 2022
9600950
fix some codacy warnings
eltimen Jun 23, 2022
42665a6
improve error messages in .item_by_path() method (UIA and WPF)
eltimen Jun 23, 2022
97e6667
update submodule
eltimen Jun 24, 2022
30a2b29
fix missing pid in __getattribute__ of WindowsSpecification from wrapper
eltimen Jun 24, 2022
946316e
switch injected submodule to upstream repo
eltimen Jun 27, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
4 changes: 4 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
[submodule "pywinauto/windows/injected"]
path = pywinauto/windows/injected
url = https://github.com/pywinauto/injected.git
branch = master
7 changes: 5 additions & 2 deletions appveyor.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
#

# fetch repository as a zip archive
shallow_clone: true # default is "false"
shallow_clone: false # default is "false"

environment:

Expand Down Expand Up @@ -67,6 +67,10 @@ install:
# as well as pywin32, pillow and coverage
- "powershell ./ci/install.ps1"
- "SET PATH=%PYTHON%;%PYTHON%\\Scripts;%PATH%"
- cd %APPVEYOR_BUILD_FOLDER%
- git submodule init
- git submodule update


# Install the build dependencies of the project. If some dependencies contain
# compiled extensions and are not provided as pre-built wheel packages,
Expand All @@ -76,7 +80,6 @@ install:

# Enable desktop (for correct screenshots).
#- ps: iex ((new-object net.webclient).DownloadString('https://raw.githubusercontent.com/appveyor/ci/master/scripts/enable-desktop.ps1'))

build: false # Not a C# project, build stuff at the test step instead.

test_script:
Expand Down
14 changes: 12 additions & 2 deletions pywinauto/base_application.py
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,8 @@ def __find_base(self, criteria_, timeout, retry_interval):
if 'backend' not in ctrl_criteria:
ctrl_criteria['backend'] = self.backend.name

if self.app is not None:
ctrl_criteria['pid'] = self.app.process
ctrl = self.backend.generic_wrapper_class(findwindows.find_element(**ctrl_criteria))
previous_parent = ctrl.element_info
ctrls.append(ctrl)
Expand Down Expand Up @@ -479,7 +481,6 @@ def by(self, **criteria):
new_item = WindowSpecification(self.criteria[0], allow_magic_lookup=self.allow_magic_lookup)
new_item.criteria.extend(self.criteria[1:])
new_item.criteria.append(criteria)

return new_item

def __getitem__(self, key):
Expand Down Expand Up @@ -574,7 +575,16 @@ def __getattribute__(self, attr_name):
# FIXME - I don't get this part at all, why is it win32-specific and why not keep the same logic as above?
# if we have been asked for an attribute of the dialog
# then resolve the window and return the attribute
desktop_wrapper = self.backend.generic_wrapper_class(self.backend.element_info_class())
if self.backend.name in ('wpf'):
if self.app is None and len(self.criteria) > 0 and 'pid' in self.criteria[0]:
pid = self.criteria[0]['pid']
else:
pid = self.app.process
desktop_wrapper = self.backend.generic_wrapper_class(
self.backend.element_info_class(pid=pid)
)
else:
desktop_wrapper = self.backend.generic_wrapper_class(self.backend.element_info_class())
need_to_resolve = (len(self.criteria) == 1 and hasattr(desktop_wrapper, attr_name))
if hasattr(self.backend, 'dialog_class'):
need_to_resolve = need_to_resolve and hasattr(self.backend.dialog_class, attr_name)
Expand Down
2 changes: 2 additions & 0 deletions pywinauto/base_wrapper.py
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,8 @@ def by(self, **criteria):

criteria['backend'] = self.backend.name
criteria['parent'] = self.element_info
if self.backend.name in ('wpf'):
criteria['pid'] = self.element_info.pid
child_specification = WindowSpecification(criteria)

return child_specification
Expand Down
3 changes: 3 additions & 0 deletions pywinauto/controls/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,5 +48,8 @@
from . import common_controls
from . import win32_controls

from . import wpfwrapper
from . import wpf_controls


from ..base_wrapper import InvalidElement
9 changes: 5 additions & 4 deletions pywinauto/controls/uia_controls.py
Original file line number Diff line number Diff line change
Expand Up @@ -1126,7 +1126,7 @@ def item_by_path(self, path, exact=False):
menu_items = [p.strip() for p in path.split("->")]
items_cnt = len(menu_items)
if items_cnt == 0:
raise IndexError()
raise IndexError("Menu path has incorrect format, no item identifiers found")
for item in menu_items:
if not item:
raise IndexError("Empty item name between '->' separators")
Expand All @@ -1141,8 +1141,9 @@ def next_level_menu(parent_menu, item_name, is_last):
# a new Menu control is created and placed on the dialog. It can be
# a direct child or a descendant.
# Sometimes we need to re-discover Menu again
i = 0
try:
menu = next_level_menu(self, menu_items[0], items_cnt == 1)
menu = next_level_menu(self, menu_items[i], items_cnt == 1)
if items_cnt == 1:
return menu

Expand All @@ -1156,8 +1157,8 @@ def next_level_menu(parent_menu, item_name, is_last):

for i in range(1, items_cnt):
menu = next_level_menu(menu, menu_items[i], items_cnt == i + 1)
except(AttributeError):
raise IndexError()
except (AttributeError, IndexError):
raise IndexError('Incorrect menu path, item "{}" (index {}) not found'.format(menu_items[i], i))

return menu

Expand Down