diff --git a/AXUI/XML/__init__.py b/AXUI/XML/__init__.py index fc11608..cb478b3 100644 --- a/AXUI/XML/__init__.py +++ b/AXUI/XML/__init__.py @@ -1,4 +1,4 @@ #used by config module -from XML_config import core_config +from .XML_config import core_config #app map interface -from app_map import AppMap \ No newline at end of file +from .app_map import AppMap \ No newline at end of file diff --git a/AXUI/XML/__init__.py.bak b/AXUI/XML/__init__.py.bak new file mode 100644 index 0000000..fc11608 --- /dev/null +++ b/AXUI/XML/__init__.py.bak @@ -0,0 +1,4 @@ +#used by config module +from XML_config import core_config +#app map interface +from app_map import AppMap \ No newline at end of file diff --git a/AXUI/XML/app_map.py b/AXUI/XML/app_map.py index 5d0e6c6..65fb81c 100644 --- a/AXUI/XML/app_map.py +++ b/AXUI/XML/app_map.py @@ -7,9 +7,9 @@ from AXUI.parsing.identifier_parsing import identifier_lexer, identifier_parser from AXUI.parsing.gui_command_parsing import gui_command_lexer, gui_command_parser from AXUI.parsing.cli_command_parsing import cli_command_lexer, cli_command_parser -import func -from XML_config import core_config -import element as element_module +from . import func +from .XML_config import core_config +from . import element as element_module def singleton(class_): instances = {} @@ -75,7 +75,7 @@ def verification(self): LOGGER.debug("pyxb not install, skip app map verification") return - from validate import check_app_map + from .validate import check_app_map check_app_map(core_config.query_schema_file("AXUI_app_map.xsd"), self.app_map_xml) def _parse_variable_elements(self, root_element): diff --git a/AXUI/XML/app_map.py.bak b/AXUI/XML/app_map.py.bak new file mode 100644 index 0000000..5d0e6c6 --- /dev/null +++ b/AXUI/XML/app_map.py.bak @@ -0,0 +1,280 @@ +import os +import re +import subprocess +import xml.etree.ElementTree as ET + +from AXUI.logger import LOGGER +from AXUI.parsing.identifier_parsing import identifier_lexer, identifier_parser +from AXUI.parsing.gui_command_parsing import gui_command_lexer, gui_command_parser +from AXUI.parsing.cli_command_parsing import cli_command_lexer, cli_command_parser +import func +from XML_config import core_config +import element as element_module + +def singleton(class_): + instances = {} + def getinstance(xml, **kwargs): + full_xml = core_config.query_app_map_file(xml) + if full_xml not in instances: + #uplevel_app_map_xmls is a global value in decorater + #will overwrite it if there is argument passed in + #if no argument passed in, set to [] manually + if len(kwargs) == 0: + instances[full_xml] = class_(full_xml, uplevel_app_map_xmls=[]) + else: + instances[full_xml] = class_(full_xml, **kwargs) + return instances[full_xml] + + return getinstance + +@singleton +class AppMap(object): + '''represent a app map + one instance for one app map + app map includes UI elements, functions and other app maps + app maps should not recursive include each other + ''' + + def __init__(self, xml, uplevel_app_map_xmls): + self.app_map_xml = xml + #prevent recursive include + uplevel_app_map_xmls.append(self.app_map_xml) + self.uplevel_app_map_xmls = uplevel_app_map_xmls + self.variables = {} + self.app_maps = {} + self.funcs = {} + self.UI_elements = {} + #verify the app map using pyxb + self.verification() + #parse the app map and build objects in memory + self.parse_all() + + def __repr__(self): + docstring = "AppMap:\n" + docstring += " Include Variables:\n" + for variable in self.variables: + docstring += " %s: %s\n" % (variable, repr(self.variables[variable])) + docstring += " Include AppMaps:\n" + for appmap in self.app_maps: + docstring += " %s\n" % appmap + docstring += " Include Functions:\n" + for func_ in self.funcs: + docstring += " %s\n" % func_ + docstring += " Include UI elements:\n" + for element in self.UI_elements: + docstring += " %s\n" % element + + return docstring + + def verification(self): + '''verify the app map xml using according schema, need pyxb module + ''' + try: + import pyxb + except ImportError: + LOGGER.debug("pyxb not install, skip app map verification") + return + + from validate import check_app_map + check_app_map(core_config.query_schema_file("AXUI_app_map.xsd"), self.app_map_xml) + + def _parse_variable_elements(self, root_element): + variable_root = root_element.find("AXUI:variables", namespaces={"AXUI":"AXUI"}) + if variable_root is not None: + for variable_element in variable_root.findall("AXUI:variable", namespaces={"AXUI":"AXUI"}): + name = variable_element.attrib["name"] + value = variable_element.attrib["value"] + self.variables[name] = value + + def _parse_include_elements(self, root_element): + include_root = root_element.find("AXUI:includes", namespaces={"AXUI":"AXUI"}) + if include_root is not None: + for include_element in include_root.findall("AXUI:include", namespaces={"AXUI":"AXUI"}): + name = include_element.attrib["name"] + path = include_element.attrib["path"] + #prevent recursive include + if core_config.query_app_map_file(path) in self.uplevel_app_map_xmls: + raise ValueError("Recursive include for app map: %s" % core_config.query_app_map_file(path)) + self.app_maps[name] = AppMap(path, uplevel_app_map_xmls=self.uplevel_app_map_xmls) + + def _parse_func_elements(self, root_element): + func_root = root_element.find("AXUI:funcs", namespaces={"AXUI":"AXUI"}) + if func_root is not None: + for func_element in func_root.findall("AXUI:func", namespaces={"AXUI":"AXUI"}): + name = func_element.attrib["name"] + self.funcs[name] = func.Func(func_element, self) + + def _init_UI_element(self, xml_element): + if xml_element.tag == "{AXUI}UI_element": + UI_element = element_module.Element() + + UI_element.name = xml_element.attrib["name"] + if "timeout" in xml_element.attrib: + UI_element.timeout = float(xml_element.attrib["timeout"]) + if "parent" in xml_element.attrib: + UI_element.parent_string = xml_element.attrib["parent"] + if "start_func" in xml_element.attrib: + UI_element.start_func = self.get_func_by_name(xml_element.attrib["start_func"]) + if "stop_func" in xml_element.attrib: + UI_element.stop_func = self.get_func_by_name(xml_element.attrib["stop_func"]) + if "identifier" in xml_element.attrib: + UI_element.identifier_string = xml_element.attrib["identifier"] + UI_element.identifier = identifier_parser.parse(UI_element.identifier_string, lexer=identifier_lexer) + + return UI_element + + elif xml_element.tag == "{AXUI}Root_element": + UI_element = element_module.RootElement() + + UI_element.name = xml_element.attrib["name"] + + return UI_element + + elif xml_element.tag == "{AXUI}UI_element_group": + UI_element_group = element_module.ElementGroup() + + UI_element_group.name = xml_element.attrib["name"] + if "timedelay" in xml_element.attrib: + UI_element_group.timedelay = float(xml_element.attrib["timedelay"]) + if "parent" in xml_element.attrib: + UI_element_group.parent_string = xml_element.attrib["parent"] + if "start_func" in xml_element.attrib: + UI_element_group.start_func = self.get_func_by_name(xml_element.attrib["start_func"]) + if "stop_func" in xml_element.attrib: + UI_element_group.stop_func = self.get_func_by_name(xml_element.attrib["stop_func"]) + if "identifier" in xml_element.attrib: + UI_element_group.identifier_string = xml_element.attrib["identifier"] + UI_element_group.identifier = identifier_parser.parse(UI_element_group.identifier_string, lexer=identifier_lexer) + + return UI_element_group + + def _build_UI_element(self, xml_element, parent_UI_element): + UI_element = self._init_UI_element(xml_element) + + UI_element.parent = parent_UI_element + parent_UI_element.children[UI_element.name] = UI_element + + return UI_element + + def _build_top_level_UI_element(self, xml_element): + UI_element = self._init_UI_element(xml_element) + + #do nothing for root element + if isinstance(UI_element, element_module.RootElement): + return UI_element + + #top level element must have parent + if UI_element.parent_string: + UI_element.parent = self.get_UI_element_by_name(UI_element.parent_string) + else: + raise ValueError("Top level element must have parent attribute, miss parent in element: %s" % UI_element.name) + + return UI_element + + def _build_children_UI_elements(self, parent_xml_element, parent_element): + for xml_element in list(parent_xml_element): + UI_element = self._build_UI_element(xml_element, parent_element) + self._build_children_UI_elements(xml_element, UI_element) + + def _parse_UI_elements(self, root_element): + UI_element_root = root_element.find("AXUI:UI_elements", namespaces={"AXUI":"AXUI"}) + if UI_element_root is not None: + for xml_element in list(UI_element_root): + UI_element = self._build_top_level_UI_element(xml_element) + self.UI_elements[UI_element.name] = UI_element + self._build_children_UI_elements(xml_element, UI_element) + + def parse_all(self): + '''parse all elements (app_maps, funcs, UI_elements) in the app map + ''' + element_tree = ET.parse(self.app_map_xml) + root_element = element_tree.getroot() + + self._parse_variable_elements(root_element) + self._parse_include_elements(root_element) + self._parse_func_elements(root_element) + self._parse_UI_elements(root_element) + + def _get_object_by_name_list(self, name_list): + parent = self + for name in name_list: + object_ = getattr(parent, name) + parent = object_ + + return object_ + + def get_include_app_map_by_name(self, name_list): + '''get app_map by name list + name_list should be like "app_map1.app_map2...app_mapX" + ''' + object_ = self._get_object_by_name_list(name_list.split(".")) + if not isinstance(object_, AppMap): + raise ValueError("Expect app map, get %s, please check your name and app map" % type(object_)) + + return object_ + + def get_UI_element_by_name(self, name_list): + '''get element by name + name_list should be like "app_map1.app_map2...element1.element2...elementX" + ''' + object_ = self._get_object_by_name_list(name_list.split(".")) + if not (isinstance(object_, element_module.Element) or isinstance(object_, element_module.RootElement)): + raise ValueError("Expect UI element, get %s, please check your name and app map" % type(object_)) + return object_ + + def get_func_by_name(self, name_list): + '''get func by name + name_list should be like "app_map1.app_map2...app_mapX...func_name" + ''' + object_ = self._get_object_by_name_list(name_list.split(".")) + if not isinstance(object_, func.Func): + raise ValueError("Expect func, get %s, please check your name and app map" % type(object_)) + + return object_ + + def gui_execute(self, command): + '''execute gui command + command like "app_map1.app_map2...element1.element2...operation [parameter1 parameter2 ...]" + ''' + (object_name_list, parameter_list) = gui_command_parser.parse(command, lexer=gui_command_lexer) + object_= self._get_object_by_name_list(object_name_list) + LOGGER.debug("GUI execute %s %s" , object_name_list, parameter_list) + object_(*parameter_list) + + def cli_execute(self, command): + '''execute cli command + command like "app parameter1 parameter 2" + may contain variables + ''' + args = cli_command_parser.parse(command, lexer=cli_command_lexer) + #replace variables + for i, arg in enumerate(args): + if not re.match("^{.*}$", arg) is None: + args[i] = self.variables[arg.strip("{").strip("}")] + + #some app need to execute in their folder + app_path = os.path.dirname(args[0]) + if app_path: + os.chdir(app_path) + + LOGGER.debug("CLI execute: %s" , repr(args)) + p = subprocess.Popen(args, shell=True) + #some app is blocking, do not wait here + #p.communicate() + #return p.returncode + + def __getattr__(self, name): + '''get app_map attribute + will query object from app_maps > UI_elements > funcs + ''' + if name in self.app_maps: + return_object = self.app_maps[name] + elif name in self.UI_elements: + return_object = self.UI_elements[name] + elif name in self.funcs: + return_object = self.funcs[name] + else: + raise AttributeError("App map: %s don't have attribute: %s" % (self.app_map_xml, name)) + + return return_object + diff --git a/AXUI/XML/element.py b/AXUI/XML/element.py index 14b1be8..face6d5 100644 --- a/AXUI/XML/element.py +++ b/AXUI/XML/element.py @@ -4,7 +4,7 @@ from AXUI.logger import LOGGER, logger_config from AXUI.driver import get_driver from AXUI.exceptions import DriverException, TimeOutError -from XML_config import core_config +from .XML_config import core_config class FakeUIElement(object): '''used for Elements without identifier @@ -151,7 +151,7 @@ def screenshot(self, screenshot_location = ""): return absfile def __getattr__(self, name): - if self.children.has_key(name): + if name in self.children: return self.children[name] else: self.start() @@ -432,7 +432,7 @@ def screenshot(self, screenshot_location = ""): return absfile def __getattr__(self, name): - if self.children.has_key(name): + if name in self.children: return self.children[name] else: self.start() diff --git a/AXUI/XML/element.py.bak b/AXUI/XML/element.py.bak new file mode 100644 index 0000000..14b1be8 --- /dev/null +++ b/AXUI/XML/element.py.bak @@ -0,0 +1,509 @@ + +import os +import time +from AXUI.logger import LOGGER, logger_config +from AXUI.driver import get_driver +from AXUI.exceptions import DriverException, TimeOutError +from XML_config import core_config + +class FakeUIElement(object): + '''used for Elements without identifier + ''' + def __repr__(self): + return "Fake element, Use when there is no identifier for this element" + + def verify(self): + return self + +class RootElement(object): + '''wrapper for root element + provide interface for enter point of UI automation API + like desktop of windows UIA API, or browser for web driver API + + Interfaces exposed for use: + verify: function to verify if self still exist + start: function to init and start this element + stop: function to stop this element + screenshot: function to take a screenshot + + other driver special interfaces + ''' + def __init__(self): + #Need init by app map module + self.name = "" + self.children = {} + self.parent = None + self._time_out = None + + #get driver module + driver_module = get_driver() + #represent for root interface of driver module + self.root = driver_module.Root() + + def __repr__(self): + docstring = "root element instance" + return docstring + + @property + def timeout(self): + if not self._time_out is None: + return self._time_out + else: + return core_config.timeout + + @timeout.setter + def timeout(self, input_value): + self._time_out = input_value + + @property + def screenshot_location(self): + return core_config.screenshot_location + + @property + def screenshot_on(self): + if core_config.screenshot_option == "always_on": + return True + else: + return False + + @property + def screenshot_off(self): + if core_config.screenshot_option == "always_off": + return True + else: + return False + + @property + def screenshot_on_failure(self): + if core_config.screenshot_option == "on_failure": + return True + else: + return False + + @property + def details(self): + '''return details of this element + ''' + docstring = "Root element details for: %s\n" % self.name + + docstring += "#"*24 + docstring += "\n" + docstring += " Children:\n" + for key in self.children: + docstring += " %s\n" % repr(self.children[key]) + + docstring += "#"*24 + docstring += "\n" + docstring += " details:\n" + docstring += self.root.__repr__() + + return docstring + + def verify(self): + '''verify UIElement is valid or not + return None if not valid + ''' + return self.root.verify() + + def start(self, **kwargs): + ''' + start method will do some initiation for root element + for windows UIA, just bind UIA root element to driver root interface, no arguments required + for webdriver API, could pass in some arguments, like browser type, URL, timeout, and start the browser + ''' + if self.verify() is None: + self.root.start(**kwargs) + + def stop(self): + ''' + ''' + if not self.verify() is None: + self.root.stop() + + def find_element(self, identifier): + '''find element by identifier + identifier should already be parsed + ''' + self.start() + return self.root.find_element(identifier) + + def find_elements(self, identifier): + '''find all elements match identifier + ''' + self.start() + return self.root.find_elements(identifier) + + def screenshot(self, screenshot_location = ""): + '''take a screen shot for this element + ''' + if not os.path.isdir(screenshot_location): + screenshot_location = self.screenshot_location + + self.start() + + filename = self.name+"_"+str(time.time())+".bmp" + absfile = os.path.join(screenshot_location, filename) + if os.path.isfile(absfile): + os.remove(absfile) + + self.root.screenshot(absfile) + LOGGER.info("Screenshot taken: %s" , absfile) + return absfile + + def __getattr__(self, name): + if self.children.has_key(name): + return self.children[name] + else: + self.start() + return getattr(self.root, name) + +class Element(object): + '''wrapper for UIElement + hold informations from app map, and provide UIElement interface for app map + + Attributes used internal: + name: Element's name, from XML + parent_string: Element's parent string, from XML + identifier_string: Identifier string, from XML + timeout: Time out for this element, from XML + children: children elements + parent: parent element + start_func: function to start this element + stop_func: function to stop this element + identifier: parsed identifier + + UIElement: driver interface for UIElement + find: function to find first match child element + findall: function to find all matched children elements + + Interfaces exposed for use: + verify: function to verify if self still exist + start: function to start this element + stop: function to stop this element + screenshot: function to take a screenshot + + other driver special interfaces + + ''' + #fake UI element is for elements without identifier + fake_UI_element = FakeUIElement() + + def __init__(self): + #Need init by app map + self.name = "" + self.parent_string = "" + self.identifier_string = "" + self._time_out = None + self.children = {} + self.parent = None + self.start_func = None + self.stop_func = None + self.identifier = None + + #UIElement is assigned by verify method + self.UIElement = None + + def __repr__(self): + docstring = "element instance for: %s" % self.name + return docstring + + @property + def timeout(self): + if not self._time_out is None: + return self._time_out + else: + return core_config.time_out + + @timeout.setter + def timeout(self, input_value): + self._time_out = input_value + + @property + def screenshot_location(self): + return core_config.screenshot_location + + @property + def screenshot_on(self): + if core_config.screenshot_option == "always_on": + return True + else: + return False + + @property + def screenshot_off(self): + if core_config.screenshot_option == "always_off": + return True + else: + return False + + @property + def screenshot_on_failure(self): + if core_config.screenshot_option == "on_failure": + return True + else: + return False + + @property + def details(self): + '''return details of this element + ''' + docstring = "Element details for: %s\n" % self.name + + docstring += "#"*24 + docstring += "\n" + docstring += " Parent: %s\n" % repr(self.parent) + + docstring += "#"*24 + docstring += "\n" + docstring += " Children:\n" + for key in self.children: + docstring += " %s\n" % repr(self.children[key]) + + docstring += "#"*24 + docstring += "\n" + docstring += " UIElement details:\n" + + if self.verify() is None: + docstring += " UIElement not init or stopped for this Element\n" + else: + docstring += self.UIElement.__repr__() + + return docstring + + def verify(self): + '''verify UIElement is valid or not + return None if not valid + ''' + #no identifier + if self.identifier is None: + self.UIElement = self.fake_UI_element + #has identifier + else: + self.UIElement = self.parent.find_element(self.identifier) + + return self.UIElement + + def find_element(self, identifier): + '''find element by identifier + identifier should already be parsed + ''' + try: + if self.UIElement is self.fake_UI_element: + return self.parent.find_element(identifier) + else: + return self.UIElement.find_element(identifier) + except DriverException: + #try to update self UIElement and find again + try: + self.verify() + if self.UIElement is self.fake_UI_element: + return self.parent.find_element(identifier) + else: + return self.UIElement.find_element(identifier) + except DriverException: + #still fail + return None + + def find_elements(self, identifier): + '''find all elements match identifier + ''' + try: + if self.UIElement is self.fake_UI_element: + return self.parent.find_elements(identifier) + else: + return self.UIElement.find_elements(identifier) + except DriverException: + #try to update self UIElement and find again + try: + self.verify() + if self.UIElement is self.fake_UI_element: + return self.parent.find_elements(identifier) + else: + return self.UIElement.find_elements(identifier) + except DriverException: + #still fail + return [] + + def _wait_start(self): + '''wait until UIElement is valid or timeout + ''' + #keep finding the element by identifier, until found or timeout + LOGGER.info("Waiting Element show up, timeout %s", self.timeout) + start_time = time.time() + while True: + current_time = time.time() + self.UIElement = self.verify() + if not self.UIElement is None: + LOGGER.info("Find element: %s after waiting %ss, continue", self.name, current_time - start_time) + #do a desktop screenshot here as required + if self.screenshot_on: + #get root + element = self + parent = element.parent + while not parent is None: + element = parent + parent = element.parent + #screenshot + element.screenshot() + return + + time.sleep(0.1) + if current_time - start_time > self.timeout: + #do a desktop screenshot here as required + if not self.screenshot_off: + #get root + element = self + parent = element.parent + while not parent is None: + element = parent + parent = element.parent + #screenshot + element.screenshot() + raise TimeOutError("time out encounter, during start element:%s" % self.name) + + def start(self): + '''start and find this UIElement + ''' + #need to start parent element first + self.parent.start() + if self.verify() is None: + LOGGER.info("Start function triggered, due to cannot find element: %s", self.name) + #run start func + if self.start_func: + self.start_func.run() + else: + LOGGER.info("Element doesn't have start function") + self._wait_start() + else: + LOGGER.info("Find element: %s, continue", self.name) + + def _wait_stop(self): + '''wait until UIElement is not valid or timeout + ''' + LOGGER.info("Waiting Element to stop, timeout %s" % self.timeout) + if not self.identifier is None: + #keep verify the element, until not found or timeout + start_time = time.time() + while True: + self.UIElement = self.verify() + if self.UIElement is None: + return + + time.sleep(0.1) + current_time = time.time() + if current_time - start_time > self.timeout: + raise TimeOutError("time out encounter, during stop element:%s" % self.name) + + def stop(self): + '''stop and verify this UIElement + Need to stop all children first + ''' + if not self.verify() is None: + #stop all children + for name in self.children: + self.children[name].stop() + + #stop self + #only stop and check element which has stop_func attribute + LOGGER.info("Find element %s, trigger stop function", self.name) + if self.stop_func: + self.stop_func.run() + else: + LOGGER.info("Element doesn't have stop function") + self._wait_stop() + else: + LOGGER.info("Not find element %s, continue", self.name) + + def screenshot(self, screenshot_location = ""): + '''take a screen shot for this element + ''' + if not os.path.isdir(screenshot_location): + screenshot_location = self.screenshot_location + + self.start() + + filename = self.name+"_"+str(time.time())+".bmp" + absfile = os.path.join(screenshot_location, filename) + if os.path.isfile(absfile): + os.remove(absfile) + + self.UIElement.screenshot(absfile) + LOGGER.info("Screenshot taken: %s" , absfile) + return absfile + + def __getattr__(self, name): + if self.children.has_key(name): + return self.children[name] + else: + self.start() + return getattr(self.UIElement, name) + +class ElementGroup(object): + ''' + ''' + def __init__(self): + #Need init by app map + self.name = "" + self.parent_string = "" + self.identifier_string = "" + #move this to config later + self.timedelay = 2 + self.parent = None + self.start_func = None + self.stop_func = None + self.identifier = None + + #Elements is assigned dynamicly during runtime + self.Elements = [] + + def __repr__(self): + docstring = "elementgroup instance for: %s" % self.name + return docstring + + def start(self): + '''start and findall UIElements, build Elements automatically + ''' + #need to start parent element first + self.parent.start() + #run start func + if self.start_func: + self.start_func.run() + #delay some time + time.sleep(self.timedelay) + #find and create all matched elements + UIElements = self.parent.find_elements(self.identifier) + + for i, UIElement in enumerate(UIElements): + element = Element() + element.parent = self.parent + if self.identifier is None: + element.identifier = ["Index", i] + else: + element.identifier = ("AND", self.identifier, ("Index", i)) + element.UIElement = UIElement + #Name property is not available for appium driver + #element.name = UIElement.Name + + self.Elements.append(element) + + return self.Elements + + def stop(self): + '''stop this Element group + ''' + #stop self + #only stop element which has stop_func attribute + if self.stop_func: + self.stop_func.run() + + def __getitem__(self, index): + self.start() + return self.Elements[index] + + def __iter__(self): + self.start() + return iter(self.Elements) + + def __len__(self): + self.start() + return len(self.Elements) diff --git a/AXUI/__init__.py b/AXUI/__init__.py index 81e4803..40452c9 100644 --- a/AXUI/__init__.py +++ b/AXUI/__init__.py @@ -1,12 +1,12 @@ #port Config -from config import Config +from .config import Config #port Logger -from logger import LOGGER +from .logger import LOGGER #port AppMap -from XML import AppMap +from .XML import AppMap #Asserts here def AssertIsValid(element, msg="element not valid"): @@ -18,7 +18,7 @@ def AssertIsValid(element, msg="element not valid"): def AssertIsImageSimiliar(image1, image2, diff_percentage_required=0.1, msg="images not similiar"): '''check if two image is simililar ''' - from image import image_compare + from .image import image_compare diff_percentage = image_compare(image1, image2) if diff_percentage > diff_percentage_required: raise AssertionError(msg) diff --git a/AXUI/__init__.py.bak b/AXUI/__init__.py.bak new file mode 100644 index 0000000..81e4803 --- /dev/null +++ b/AXUI/__init__.py.bak @@ -0,0 +1,27 @@ + +#port Config +from config import Config + +#port Logger +from logger import LOGGER + +#port AppMap +from XML import AppMap + +#Asserts here +def AssertIsValid(element, msg="element not valid"): + '''check if element valid + ''' + if element.verify() is None: + raise AssertionError(msg) + +def AssertIsImageSimiliar(image1, image2, diff_percentage_required=0.1, msg="images not similiar"): + '''check if two image is simililar + ''' + from image import image_compare + diff_percentage = image_compare(image1, image2) + if diff_percentage > diff_percentage_required: + raise AssertionError(msg) + + +__all__=["Config", "LOGGER", "AppMap", "AssertIsValid", "AssertIsImageSame"] diff --git a/AXUI/config.py b/AXUI/config.py index 5c35e69..1e8f877 100644 --- a/AXUI/config.py +++ b/AXUI/config.py @@ -2,18 +2,18 @@ config module ''' -import ConfigParser +import configparser -from XML import core_config -from driver import driver_config -from image import image_config -from logger import logger_config +from .XML import core_config +from .driver import driver_config +from .image import image_config +from .logger import logger_config class GlobalConfig(object): supported_sections = ["core", "driver", "image", "logger"] def load_config_file(self, config_file): - raw_parser = ConfigParser.RawConfigParser() + raw_parser = configparser.RawConfigParser() raw_parser.read(config_file) for section in raw_parser.sections(): diff --git a/AXUI/config.py.bak b/AXUI/config.py.bak new file mode 100644 index 0000000..5c35e69 --- /dev/null +++ b/AXUI/config.py.bak @@ -0,0 +1,32 @@ +''' +config module +''' + +import ConfigParser + +from XML import core_config +from driver import driver_config +from image import image_config +from logger import logger_config + + +class GlobalConfig(object): + supported_sections = ["core", "driver", "image", "logger"] + def load_config_file(self, config_file): + raw_parser = ConfigParser.RawConfigParser() + raw_parser.read(config_file) + + for section in raw_parser.sections(): + if section in self.supported_sections: + module = eval(section+"_config") + for option in raw_parser.options(section): + if hasattr(module, option): + setattr(module, option, raw_parser.get(section, option)) + + def __getattr__(self, item): + if item in self.supported_sections: + return eval(item+"_config") + else: + raise AttributeError("No config for %s" % item) + +Config = GlobalConfig() \ No newline at end of file diff --git a/AXUI/driver/__init__.py b/AXUI/driver/__init__.py index 54cad09..213346e 100644 --- a/AXUI/driver/__init__.py +++ b/AXUI/driver/__init__.py @@ -1,4 +1,4 @@ -from config_driver import driver_config +from .config_driver import driver_config from AXUI.logger import LOGGER def get_driver(): @@ -9,7 +9,7 @@ def get_driver(): import sys, os sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) driver = __import__(driver_config.driver_used+"_driver") - except ImportError, e: + except ImportError as e: LOGGER.error("Driver load error: %s" % driver_config.driver_used) raise e diff --git a/AXUI/driver/__init__.py.bak b/AXUI/driver/__init__.py.bak new file mode 100644 index 0000000..54cad09 --- /dev/null +++ b/AXUI/driver/__init__.py.bak @@ -0,0 +1,16 @@ +from config_driver import driver_config +from AXUI.logger import LOGGER + +def get_driver(): + '''get driver + Return: return driver module selected in config + ''' + try: + import sys, os + sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) + driver = __import__(driver_config.driver_used+"_driver") + except ImportError, e: + LOGGER.error("Driver load error: %s" % driver_config.driver_used) + raise e + + return driver \ No newline at end of file diff --git a/AXUI/driver/appium_driver/UIElement.py b/AXUI/driver/appium_driver/UIElement.py index 6d36703..5513959 100644 --- a/AXUI/driver/appium_driver/UIElement.py +++ b/AXUI/driver/appium_driver/UIElement.py @@ -7,14 +7,14 @@ try: import appium.webdriver as webdriver -except ImportError, e: +except ImportError as e: LOGGER.error("To use AXUI appium driver, you must install selenium and appium python client first, check https://pypi.python.org/pypi/selenium, https://pypi.python.org/pypi/Appium-Python-Client") raise e from selenium.common.exceptions import NoSuchElementException from selenium.webdriver.common.desired_capabilities import DesiredCapabilities from selenium.webdriver.common.keys import Keys -import Translater +from . import Translater class Keyboard(object): def __init__(self, selenium_element): @@ -41,7 +41,7 @@ def input(self, *values): key = value.lstrip("{").rstrip("}") try: key_value = getattr(Keys, key) - except AttributeError, e: + except AttributeError as e: LOGGER.warning("Input special key not support: %s, skip this input" , key) else: translated_values.append(key_value) diff --git a/AXUI/driver/appium_driver/UIElement.py.bak b/AXUI/driver/appium_driver/UIElement.py.bak new file mode 100644 index 0000000..6d36703 --- /dev/null +++ b/AXUI/driver/appium_driver/UIElement.py.bak @@ -0,0 +1,416 @@ + +import re +import inspect + +from AXUI.logger import LOGGER +from AXUI.exceptions import DriverException + +try: + import appium.webdriver as webdriver +except ImportError, e: + LOGGER.error("To use AXUI appium driver, you must install selenium and appium python client first, check https://pypi.python.org/pypi/selenium, https://pypi.python.org/pypi/Appium-Python-Client") + raise e + +from selenium.common.exceptions import NoSuchElementException +from selenium.webdriver.common.desired_capabilities import DesiredCapabilities +from selenium.webdriver.common.keys import Keys +import Translater + +class Keyboard(object): + def __init__(self, selenium_element): + self.selenium_element = selenium_element + + def input(self, *values): + '''send keyboard input to UI element + + Keyboard input string: + (1) For normal charactors like [0~9][a~z][A~Z], input directly + (2) For special charactors like "space", "tab", "newline", "F1~F12" + You use {key_name} to replace them, all support keys in "selenium/webdriver/common/keys " + ''' + translated_values = [] + for value in values: + #still support selenium Keys object + if isinstance(value, Keys): + translated_values.append(value) + elif isinstance(value, int): + translated_values.append(str(value)) + elif isinstance(value, str): + #handle special keys + if re.match("^{.*}$", value) != None: + key = value.lstrip("{").rstrip("}") + try: + key_value = getattr(Keys, key) + except AttributeError, e: + LOGGER.warning("Input special key not support: %s, skip this input" , key) + else: + translated_values.append(key_value) + else: + translated_values.append(value) + + self.selenium_element.send_keys(*translated_values) + +class Mouse(object): + def __init__(self, selenium_element): + self.selenium_element = selenium_element + + def left_click(self): + self.selenium_element.click() + +class Touch(object): + def __init__(self, selenium_element): + self.selenium_element = selenium_element + + ############################ + #porting appium touch methods + ############################ + def scroll(self, origin_el, destination_el): + self.selenium_element.scroll(origin_el.selenium_element, destination_el.selenium_element) + + def drag_and_drop(self, origin_el, destination_el): + self.selenium_element.drag_and_drop(origin_el.selenium_element, destination_el.selenium_element) + + def tap(self, positions, duration=None): + self.selenium_element.tap(positions, duration) + + def swipe(self, start_x, start_y, end_x, end_y, duration=None): + self.selenium_element.swipe(start_x, start_y, end_x, end_y, duration) + + def flick(self, start_x, start_y, end_x, end_y): + self.selenium_element.flick(start_x, start_y, end_x, end_y) + + def pinch(self, element=None, percent=200, steps=50): + self.selenium_element.pinch(element.selenium_element, percent, steps) + + def zoom(self, element=None, percent=200, steps=50): + self.selenium_element.zoom(element.selenium_element, percent, steps) + +class NormalPattern(object): + ''' + pattern interface for browser + ''' + interfaces = [ + "submit", + "clear", + + "is_selected", + "is_enabled", + "is_displayed", + + "value_of_css_property", + ] + + def __init__(self, selenium_element): + self.selenium_element = selenium_element + + def __getattr__(self, name): + if name in self.interfaces: + return getattr(self.selenium_element, name) + else: + LOGGER.debug("This method not exist in NormalPattern: %s", name) + +class BrowserPattern(object): + ''' + pattern interface for browser + ''' + interfaces = [ + "get", + "close", + "maximize_window", + + "execute_script", + "execute_async_script", + "set_script_timeout", + + "back", + "forward", + "refresh", + + "get_cookies", + "get_cookie", + "delete_cookie", + "delete_all_cookies", + "add_cookie", + + "implicitly_wait", + "set_page_load_timeout", + + "set_window_size", + "get_window_size", + "set_window_position", + "get_window_position", + + "get_log", + ] + + def __init__(self, selenium_element): + self.selenium_element = selenium_element + + def __getattr__(self, name): + if name in self.interfaces: + return getattr(self.selenium_element, name) + else: + LOGGER.debug("This method not exist in BrowserPattern: %s", name) + +class MobilePattern(object): + ''' + pattern interface for mobile root element + ''' + #interfaces may change due to appium interface maybe change + interfaces = [ + + "scroll", + "drag_and_drop", + "tap", + "swipe", + "flick", + "pinch", + "zoom", + + "reset", + + "pull_file", + "pull_folder", + "push_file", + + "background_app", + "is_app_installed", + "install_app", + "remove_app", + "launch_app", + "close_app", + + "start_activity", + + "lock", + "shake", + "open_notifications", + "set_network_connection", + + "is_ime_active", + "activate_ime_engine", + "deactivate_ime_engine", + + "get_settings", + "update_settings", + + "toggle_location_services", + + ] + + def __init__(self, selenium_element): + self.selenium_element = selenium_element + + def __getattr__(self, name): + if name in self.interfaces: + return getattr(self.selenium_element, name) + else: + LOGGER.debug("This method not exist in MobileRootPattern: %s", name) + +class UIElement(object): + '''This class defines interfaces for common UI element + + Every driver (Windows, Appium, Selenium) should implement this interfaces, + provides independent interfaces for uplevel modules, so we transplant AXUI cross different platform + + Attributes: + find_element: find the first descendant element which matches parsed_identifier + find_elements: find all elements which match parsed_identifier + verify: verify current element is valid + + get_keyboard: class for keyboard related methods + get_mouse: class for mouse related methods + get_touch: class for touch related methods + + get_property: get property value for current element + get_pattern: get pattern interface for current element + ''' + + def __init__(self, selenium_element): + self.selenium_element = selenium_element + + def find_element(self, parsed_identifier): + ''' + find the first child UI element via identifier, return one UIAElement if success, return None if not find + ''' + translated_identifier = Translater.ID_Translater(parsed_identifier).get_translated() + try: + selenium_element = self.selenium_element.find_element(by=translated_identifier[0], value=translated_identifier[1]) + except NoSuchElementException: + LOGGER.debug("Cannot find target element") + return None + else: + return UIElement(selenium_element) + + def find_elements(self, parsed_identifier): + ''' + find the child UI elements via identifier, return a list containing target UI elements + ''' + translated_identifier = Translater.ID_Translater(parsed_identifier).get_translated() + elements = self.selenium_element.find_elements(by=translated_identifier[0], value=translated_identifier[1]) + + UIElements = [] + for element in elements: + UIElements.append(UIElement(element)) + + return UIElements + + def get_property(self, name): + ''' + get property value + ''' + try: + obj = getattr(self.selenium_element, name) + except AttributeError: + LOGGER.debug("Cannot find this attribute: %s" , name) + if hasattr(self.selenium_element, "get_attribute"): + LOGGER.debug("Try get_attribute method") + return self.selenium_element.get_attribute(name) + else: + if inspect.ismethod(obj): + LOGGER.debug("This is a method, not a property: %s" , name) + return None + else: + return obj + + def get_pattern(self, name): + ''' + pattern is a class support one kind of UI actions + ''' + if name == "WebUIElementPattern": + return NormalPattern(self.selenium_element) + else: + return None + + def get_keyboard(self): + ''' + get keyboard class to use keyboard related methods + ''' + return Keyboard(self.selenium_element) + + def get_mouse(self): + ''' + get mouse class to use mouse related methods + ''' + return Mouse(self.selenium_element) + + def get_touch(self): + ''' + get touch class to use touch related methods + ''' + return Touch(self.selenium_element) + + def __getattr__(self, name): + if name == "Keyboard": + return self.get_keyboard() + elif name == "Mouse": + return self.get_mouse() + elif name == "Touch": + return self.get_touch() + else: + attr = self.get_property(name) + if attr is not None: + return attr + attr = self.get_pattern(name) + if attr is not None: + return attr + raise AttributeError("Attribute not exist: %s" % name) + +class Root(UIElement): + ''' + root is the entry point to interact with UI + like desktop of windows UIA, web browser of web driver API + + This class defines interfaces for root element + + Every driver (Windows, Appium, Selenium) should implement this interfaces, + provides independent interfaces for uplevel modules, so we transplant AXUI cross different platform + + Attributes: + start: start root element + stop: stop root element + screenshot: take a screen shot for root element + + find_element: find the first descendant element which matches parsed_identifier + find_elements: find all elements which match parsed_identifier + verify: verify current element is valid + + get_keyboard: class for keyboard related methods + get_mouse: class for mouse related methods + get_touch: class for touch related methods + + get_property: get property value for current element + get_pattern: get pattern interface for current element + ''' + + def __init__(self): + self.webdriver = None + + @property + def selenium_element(self): + return self.webdriver + + def start(self, **kwargs): + ''' + get root ready + like get root element in windows UIA, get browser to target website + + must have a "browser_name" argument in kwargs to indicate which browser to use + other kwargs are same as normal selenium webdrivers + ''' + if "command_executor" not in kwargs: + #use default command executor + kwargs["command_executor"] = 'http://127.0.0.1:4444/wd/hub' + + self.webdriver = webdriver.Remote(**kwargs) + + def stop(self, **kwargs): + ''' + stop root + like close browser for web driver API + ''' + self.webdriver.quit() + + def screenshot(self, absfile_path): + ''' + take a screen shot for root + ''' + self.webdriver.get_screenshot_as_file(absfile_path) + + def verify(self): + ''' + verify if session exist, not check for appium + ''' + return self.webdriver + + def get_pattern(self, name): + ''' + pattern is a class support one kind of UI actions + ''' + if name == "BrowserPattern": + return BrowserPattern(self.selenium_element) + elif name == "MobilePattern": + return MobilePattern(self.selenium_element) + else: + return None + + def get_keyboard(self): + ''' + get keyboard class to use keyboard related methods + ''' + LOGGER.debug("Browser not support keyboard action") + return None + + def get_mouse(self): + ''' + get mouse class to use mouse related methods + ''' + LOGGER.debug("Browser not support mouse action") + return None + + def get_touch(self): + ''' + get touch class to use touch related methods + ''' + LOGGER.debug("Browser not support touch action") + return None \ No newline at end of file diff --git a/AXUI/driver/appium_driver/__init__.py b/AXUI/driver/appium_driver/__init__.py index ed72593..c6b8a69 100644 --- a/AXUI/driver/appium_driver/__init__.py +++ b/AXUI/driver/appium_driver/__init__.py @@ -1 +1 @@ -from UIElement import UIElement, Root \ No newline at end of file +from .UIElement import UIElement, Root \ No newline at end of file diff --git a/AXUI/driver/appium_driver/__init__.py.bak b/AXUI/driver/appium_driver/__init__.py.bak new file mode 100644 index 0000000..ed72593 --- /dev/null +++ b/AXUI/driver/appium_driver/__init__.py.bak @@ -0,0 +1 @@ +from UIElement import UIElement, Root \ No newline at end of file diff --git a/AXUI/driver/fake_driver/__init__.py b/AXUI/driver/fake_driver/__init__.py index 70fcac4..be21199 100644 --- a/AXUI/driver/fake_driver/__init__.py +++ b/AXUI/driver/fake_driver/__init__.py @@ -1 +1 @@ -from UIElement import UIElement +from .UIElement import UIElement diff --git a/AXUI/driver/fake_driver/__init__.py.bak b/AXUI/driver/fake_driver/__init__.py.bak new file mode 100644 index 0000000..70fcac4 --- /dev/null +++ b/AXUI/driver/fake_driver/__init__.py.bak @@ -0,0 +1 @@ +from UIElement import UIElement diff --git a/AXUI/driver/selenium_driver/UIElement.py b/AXUI/driver/selenium_driver/UIElement.py index eda3090..37deef2 100644 --- a/AXUI/driver/selenium_driver/UIElement.py +++ b/AXUI/driver/selenium_driver/UIElement.py @@ -7,14 +7,14 @@ try: import selenium.webdriver as webdriver -except ImportError, e: +except ImportError as e: LOGGER.error("To use AXUI selenium driver, you must install selenium python project first, check https://pypi.python.org/pypi/selenium") raise e from selenium.common.exceptions import NoSuchElementException from selenium.webdriver.common.desired_capabilities import DesiredCapabilities from selenium.webdriver.common.keys import Keys -import Translater +from . import Translater class Keyboard(object): def __init__(self, selenium_element): @@ -41,7 +41,7 @@ def input(self, *values): key = value.lstrip("{").rstrip("}") try: key_value = getattr(Keys, key) - except AttributeError, e: + except AttributeError as e: LOGGER.warning("Input special key not support: %s, skip this input" , key) else: translated_values.append(key_value) diff --git a/AXUI/driver/selenium_driver/UIElement.py.bak b/AXUI/driver/selenium_driver/UIElement.py.bak new file mode 100644 index 0000000..eda3090 --- /dev/null +++ b/AXUI/driver/selenium_driver/UIElement.py.bak @@ -0,0 +1,375 @@ + +import re +import inspect + +from AXUI.logger import LOGGER +from AXUI.exceptions import DriverException + +try: + import selenium.webdriver as webdriver +except ImportError, e: + LOGGER.error("To use AXUI selenium driver, you must install selenium python project first, check https://pypi.python.org/pypi/selenium") + raise e + +from selenium.common.exceptions import NoSuchElementException +from selenium.webdriver.common.desired_capabilities import DesiredCapabilities +from selenium.webdriver.common.keys import Keys +import Translater + +class Keyboard(object): + def __init__(self, selenium_element): + self.selenium_element = selenium_element + + def input(self, *values): + '''send keyboard input to UI element + + Keyboard input string: + (1) For normal charactors like [0~9][a~z][A~Z], input directly + (2) For special charactors like "space", "tab", "newline", "F1~F12" + You use {key_name} to replace them, all support keys in "selenium/webdriver/common/keys " + ''' + translated_values = [] + for value in values: + #still support selenium Keys object + if isinstance(value, Keys): + translated_values.append(value) + elif isinstance(value, int): + translated_values.append(str(value)) + elif isinstance(value, str): + #handle special keys + if re.match("^{.*}$", value) != None: + key = value.lstrip("{").rstrip("}") + try: + key_value = getattr(Keys, key) + except AttributeError, e: + LOGGER.warning("Input special key not support: %s, skip this input" , key) + else: + translated_values.append(key_value) + else: + translated_values.append(value) + + self.selenium_element.send_keys(*translated_values) + +class Mouse(object): + def __init__(self, selenium_element): + self.selenium_element = selenium_element + + def left_click(self): + self.selenium_element.click() + +class Touch(object): + def __init__(self, selenium_element): + self.selenium_element = selenium_element + +class NormalPattern(object): + ''' + pattern interface for browser + ''' + interfaces = [ + "submit", + "clear", + + "is_selected", + "is_enabled", + "is_displayed", + + "value_of_css_property", + ] + + def __init__(self, selenium_element): + self.selenium_element = selenium_element + + def __getattr__(self, name): + if name in self.interfaces: + return getattr(self.selenium_element, name) + else: + LOGGER.debug("This method not exist in NormalPattern: %s", name) + +class BrowserPattern(object): + ''' + pattern interface for browser + ''' + interfaces = [ + "get", + "close", + "maximize_window", + + "execute_script", + "execute_async_script", + "set_script_timeout", + + "back", + "forward", + "refresh", + + "get_cookies", + "get_cookie", + "delete_cookie", + "delete_all_cookies", + "add_cookie", + + "implicitly_wait", + "set_page_load_timeout", + + "set_window_size", + "get_window_size", + "set_window_position", + "get_window_position", + + "get_log", + ] + + def __init__(self, selenium_element): + self.selenium_element = selenium_element + + def __getattr__(self, name): + if name in self.interfaces: + return getattr(self.selenium_element, name) + else: + LOGGER.debug("This method not exist in BrowserPattern: %s", name) + +class UIElement(object): + '''This class defines interfaces for common UI element + + Every driver (Windows, Appium, Selenium) should implement this interfaces, + provides independent interfaces for uplevel modules, so we transplant AXUI cross different platform + + Attributes: + find_element: find the first descendant element which matches parsed_identifier + find_elements: find all elements which match parsed_identifier + verify: verify current element is valid + + get_keyboard: class for keyboard related methods + get_mouse: class for mouse related methods + get_touch: class for touch related methods + + get_property: get property value for current element + get_pattern: get pattern interface for current element + ''' + + def __init__(self, selenium_element): + self.selenium_element = selenium_element + + def find_element(self, parsed_identifier): + ''' + find the first child UI element via identifier, return one UIAElement if success, return None if not find + ''' + translated_identifier = Translater.ID_Translater(parsed_identifier).get_translated() + try: + selenium_element = self.selenium_element.find_element(by=translated_identifier[0], value=translated_identifier[1]) + except NoSuchElementException: + LOGGER.debug("Cannot find target element") + return None + else: + return UIElement(selenium_element) + + def find_elements(self, parsed_identifier): + ''' + find the child UI elements via identifier, return a list containing target UI elements + ''' + translated_identifier = Translater.ID_Translater(parsed_identifier).get_translated() + elements = self.selenium_element.find_elements(by=translated_identifier[0], value=translated_identifier[1]) + + UIElements = [] + for element in elements: + UIElements.append(UIElement(element)) + + return UIElements + + def get_property(self, name): + ''' + get property value + ''' + try: + obj = getattr(self.selenium_element, name) + except AttributeError: + LOGGER.debug("Cannot find this attribute: %s" , name) + if hasattr(self.selenium_element, "get_attribute"): + LOGGER.debug("Try get_attribute method") + return self.selenium_element.get_attribute(name) + else: + if inspect.ismethod(obj): + LOGGER.debug("This is a method, not a property: %s" , name) + return None + else: + return obj + + def get_pattern(self, name): + ''' + pattern is a class support one kind of UI actions + ''' + if name == "WebUIElementPattern": + return NormalPattern(self.selenium_element) + else: + return None + + def get_keyboard(self): + ''' + get keyboard class to use keyboard related methods + ''' + return Keyboard(self.selenium_element) + + def get_mouse(self): + ''' + get mouse class to use mouse related methods + ''' + return Mouse(self.selenium_element) + + def get_touch(self): + ''' + get touch class to use touch related methods + ''' + return Touch(self.selenium_element) + + def __getattr__(self, name): + if name == "Keyboard": + return self.get_keyboard() + elif name == "Mouse": + return self.get_mouse() + elif name == "Touch": + return self.get_touch() + else: + attr = self.get_property(name) + if attr is not None: + return attr + attr = self.get_pattern(name) + if attr is not None: + return attr + raise AttributeError("Attribute not exist: %s" % name) + +class Root(UIElement): + ''' + root is the entry point to interact with UI + like desktop of windows UIA, web browser of web driver API + + This class defines interfaces for root element + + Every driver (Windows, Appium, Selenium) should implement this interfaces, + provides independent interfaces for uplevel modules, so we transplant AXUI cross different platform + + Attributes: + start: start root element + stop: stop root element + screenshot: take a screen shot for root element + + find_element: find the first descendant element which matches parsed_identifier + find_elements: find all elements which match parsed_identifier + verify: verify current element is valid + + get_keyboard: class for keyboard related methods + get_mouse: class for mouse related methods + get_touch: class for touch related methods + + get_property: get property value for current element + get_pattern: get pattern interface for current element + ''' + + #supported webdriver browsers, in "selenium/webdriver/__init__.py" + support_browsers = { + "FIREFOX" : webdriver.Firefox, + "CHROME" : webdriver.Chrome, + "IE" : webdriver.Ie, + "OPERA" : webdriver.Opera, + "SAFARI" : webdriver.Safari, + "PHANTOMJS" : webdriver.PhantomJS, + "ANDROID" : webdriver.Android, + "REMOTE" : webdriver.Remote, + } + + def __init__(self): + self.webdriver = None + + @property + def selenium_element(self): + return self.webdriver + + def start(self, **kwargs): + ''' + get root ready + like get root element in windows UIA, get browser to target website + + must have a "browser_name" argument in kwargs to indicate which browser to use + other kwargs are same as normal selenium webdrivers + ''' + if not "browser_name" in kwargs: + LOGGER.error("Browser name not specified") + raise DriverException("Browser name not specified") + + browser_name = kwargs["browser_name"] + if not browser_name.upper() in self.support_browsers: + LOGGER.error("Unsupported browser name: %s" , browser_name) + raise DriverException("Unsupported browser name: %s" % browser_name) + + #remove browser_name key from kwargs + del kwargs["browser_name"] + + #for ie browser, need to ignore zoom settings + if browser_name.upper() == "IE": + if "capabilities" in kwargs: + #insert "ignoreZoomSetting" in driver capabilities + caps = kwargs["capabilities"] + caps["ignoreZoomSetting"] = True + else: + #add default capabilities + caps = DesiredCapabilities.INTERNETEXPLORER + caps["ignoreZoomSetting"] = True + kwargs["capabilities"] = caps + + self.webdriver = self.support_browsers[browser_name.upper()](**kwargs) + + def stop(self, **kwargs): + ''' + stop root + like close browser for web driver API + ''' + self.webdriver.quit() + + def screenshot(self, absfile_path): + ''' + take a screen shot for root + ''' + self.webdriver.get_screenshot_as_file(absfile_path) + + def verify(self): + ''' + verify if session exist + ''' + if self.webdriver is None: + return None + + try: + getattr(self.webdriver, "title") + except AttributeError: + return None + else: + return self.webdriver + + def get_pattern(self, name): + ''' + pattern is a class support one kind of UI actions + ''' + if name == "BrowserPattern": + return BrowserPattern(self.selenium_element) + else: + return None + + def get_keyboard(self): + ''' + get keyboard class to use keyboard related methods + ''' + LOGGER.debug("Browser not support keyboard action") + return None + + def get_mouse(self): + ''' + get mouse class to use mouse related methods + ''' + LOGGER.debug("Browser not support mouse action") + return None + + def get_touch(self): + ''' + get touch class to use touch related methods + ''' + LOGGER.debug("Browser not support touch action") + return None \ No newline at end of file diff --git a/AXUI/driver/selenium_driver/__init__.py b/AXUI/driver/selenium_driver/__init__.py index ed72593..c6b8a69 100644 --- a/AXUI/driver/selenium_driver/__init__.py +++ b/AXUI/driver/selenium_driver/__init__.py @@ -1 +1 @@ -from UIElement import UIElement, Root \ No newline at end of file +from .UIElement import UIElement, Root \ No newline at end of file diff --git a/AXUI/driver/selenium_driver/__init__.py.bak b/AXUI/driver/selenium_driver/__init__.py.bak new file mode 100644 index 0000000..ed72593 --- /dev/null +++ b/AXUI/driver/selenium_driver/__init__.py.bak @@ -0,0 +1 @@ +from UIElement import UIElement, Root \ No newline at end of file diff --git a/AXUI/driver/template_driver/__init__.py b/AXUI/driver/template_driver/__init__.py index ed72593..c6b8a69 100644 --- a/AXUI/driver/template_driver/__init__.py +++ b/AXUI/driver/template_driver/__init__.py @@ -1 +1 @@ -from UIElement import UIElement, Root \ No newline at end of file +from .UIElement import UIElement, Root \ No newline at end of file diff --git a/AXUI/driver/template_driver/__init__.py.bak b/AXUI/driver/template_driver/__init__.py.bak new file mode 100644 index 0000000..ed72593 --- /dev/null +++ b/AXUI/driver/template_driver/__init__.py.bak @@ -0,0 +1 @@ +from UIElement import UIElement, Root \ No newline at end of file diff --git a/AXUI/driver/windows_driver/Translater.py b/AXUI/driver/windows_driver/Translater.py index 42fb194..48f23b2 100644 --- a/AXUI/driver/windows_driver/Translater.py +++ b/AXUI/driver/windows_driver/Translater.py @@ -4,7 +4,7 @@ import re from AXUI.logger import LOGGER from AXUI.exceptions import DriverException -import UIA +from . import UIA #Custom identifiers is defined for UI elements not properly recongnized by UIA #Custom identifiers used with UIA identifiers should its special rules diff --git a/AXUI/driver/windows_driver/Translater.py.bak b/AXUI/driver/windows_driver/Translater.py.bak new file mode 100644 index 0000000..42fb194 --- /dev/null +++ b/AXUI/driver/windows_driver/Translater.py.bak @@ -0,0 +1,96 @@ +''' +translate AXUI identifier to Windows UIA searchCondition +''' +import re +from AXUI.logger import LOGGER +from AXUI.exceptions import DriverException +import UIA + +#Custom identifiers is defined for UI elements not properly recongnized by UIA +#Custom identifiers used with UIA identifiers should its special rules +custom_identifiers=[ + #Custom UI element might not recongnized by UIA, we can use cordinate to simulate it + #"Coordinate" should be used alone, used with other identifier will be skipped + "Coordinate", + #Find element by Index from a group of elements, you need check the Index before using this identifier + #"Index" should used in top level "AND" relational identifiers, otherwise will be skipped + "Index" + ] + +#what we supprt to identify UI, used in the header of translated result +#to identify what technology used to identify the UI +supprted_identifiers=["UIA"].extend(custom_identifiers) + +class ID_Translater(object): + ''' + translate parsed identifier to acoordingly search condition + ''' + def __init__(self, parsed_identifier): + self.parsed_identifier = parsed_identifier + + def _translated_atomic_identifier(self, parsed_atomic_id): + if parsed_atomic_id[0] in UIA.UIA_automation_element_property_identifers_mapping: + return UIA.IUIAutomation_object.CreatePropertyCondition(UIA.UIA_automation_element_property_identifers_mapping[parsed_atomic_id[0]], parsed_atomic_id[1]) + elif parsed_atomic_id[0] in UIA.UIA_control_pattern_property_identifiers_mapping: + return UIA.IUIAutomation_object.CreatePropertyCondition(UIA.UIA_control_pattern_property_identifiers_mapping[parsed_atomic_id[0]], parsed_atomic_id[1]) + else: + #use no UIA identifier will be skipped + LOGGER.warn("identifier: %s not in UIA property maps" , parsed_atomic_id[0]) + return None + + def _translated_relational_identifier(self, relation, translated_id_1, translated_id_2): + if relation == "AND": + return UIA.IUIAutomation_object.CreateAndCondition(translated_id_1, translated_id_2) + elif relation == "OR": + return UIA.IUIAutomation_object.CreateOrCondition(translated_id_1, translated_id_2) + else: + raise DriverException("Get error relation id: %s" % repr(relation)) + + def _translated_identifier(self, parsed_id): + if len(parsed_id) == 3: + translated_id_1 = self._translated_identifier(parsed_id[1]) + translated_id_2 = self._translated_identifier(parsed_id[2]) + if translated_id_1 and translated_id_2: + translated = self._translated_relational_identifier(parsed_id[0], translated_id_1, translated_id_2) + elif not translated_id_1 and translated_id_2: + translated = translated_id_2 + elif translated_id_1 and not translated_id_2: + translated = translated_id_1 + else: + translated = None + elif len(parsed_id) == 2: + translated = self._translated_atomic_identifier(parsed_id) + else: + raise DriverException("Get error parsed_id: %s" % repr(parsed_id)) + + #LOGGER().debug("Get translated: %s" % repr(translated)) + return translated + + def translated_top_level_identifier(self, parsed_id): + ''' + get tanslated result from parsed identifier + ''' + #handle custom identifier here + if len(parsed_id) == 2 and parsed_id[0] == "Coordinate": + #handle "Coordinate" identifier + return parsed_id + elif len(parsed_id) == 2 and parsed_id[0] == "Index": + #handle "Index" identifier + return parsed_id + elif len(parsed_id) == 3 and parsed_id[0] == "AND" and (parsed_id[1][0] == "Index" or parsed_id[2][0] == "Index"): + #handle "Index" identifier + if parsed_id[1][0] == "Index": + return ("Index", (self._translated_identifier(parsed_id[2]), parsed_id[1][1])) + else: + return ("Index", (self._translated_identifier(parsed_id[1]), parsed_id[2][1])) + else: + return ("UIA", self._translated_identifier(parsed_id)) + + def get_translated(self): + ''' + get translated result from parsed identifier + ''' + return self.translated_top_level_identifier(self.parsed_identifier) + + + diff --git a/AXUI/driver/windows_driver/UIA.py b/AXUI/driver/windows_driver/UIA.py index 9a14a4c..a39ce26 100644 --- a/AXUI/driver/windows_driver/UIA.py +++ b/AXUI/driver/windows_driver/UIA.py @@ -960,7 +960,7 @@ class UIAException(Exception): } #Control Pattern Identifiers -UIA_control_pattern_identifers = UIA_control_pattern_interfaces.keys() +UIA_control_pattern_identifers = list(UIA_control_pattern_interfaces.keys()) #Control Pattern Availability Property Identifiers UIA_control_pattern_availability_property_identifiers = \ @@ -968,7 +968,7 @@ class UIAException(Exception): #check if enum exist in current version UIA namespace #set the value if exist -for enum in UIA_enums.items(): +for enum in list(UIA_enums.items()): enum_name = enum[0] enum_contents = enum[1] #check if enum name in current UIA namespace diff --git a/AXUI/driver/windows_driver/UIA.py.bak b/AXUI/driver/windows_driver/UIA.py.bak new file mode 100644 index 0000000..9a14a4c --- /dev/null +++ b/AXUI/driver/windows_driver/UIA.py.bak @@ -0,0 +1,1077 @@ +''' +Use comtypes to generate python wrapper for windows native UIA API +This module should keep update with latest UIA version +We current not use UIA cach feature + +Comtypes: + https://github.com/enthought/comtypes + https://pythonhosted.org/comtypes/ +Windows native UIA API: + http://msdn.microsoft.com/en-us/library/windows/desktop/ee684021(v=vs.85).aspx + http://msdn.microsoft.com/en-us/library/windows/desktop/ee671216(v=vs.85).aspx +''' + +from comtypes.client import GetModule, CreateObject +from AXUI.logger import LOGGER +import ctypes + +class UIAException(Exception): + pass + +UIA_type_lib_IID = '{944DE083-8FB8-45CF-BCB7-C477ACB2F897}' +#generate UIA python wrapper +UIA_wrapper = GetModule((UIA_type_lib_IID, 1, 0)) +#create object of IUIAutomation interface +for interface_id in [("IUIAutomation2", "CUIAutomation8"), ("IUIAutomation", "CUIAutomation")]: + IUIAutomation = getattr(UIA_wrapper, interface_id[0], None) + CUIAutomation = getattr(UIA_wrapper, interface_id[1], None) + if IUIAutomation is not None: + IUIAutomation_object = CreateObject(CUIAutomation, None, None, IUIAutomation) + break + +############################## +#UI Automation Enumerations +#http://msdn.microsoft.com/en-us/library/windows/desktop/ee671210(v=vs.85).aspx +############################## +UIA_enums = { +"ActiveEnd" : { + "ActiveEnd_None" : None, + "ActiveEnd_Start" : None, + "ActiveEnd_End" : None + }, +"AnimationStyle" : { + "AnimationStyle_LasVegasLights" : None, + "AnimationStyle_BlinkingBackground" : None, + "AnimationStyle_SparkleText" : None, + "AnimationStyle_MarchingBlackAnt" : None, + "AnimationStyle_MarchingRedAnts" : None, + "AnimationStyle_Shimmer" : None, + "AnimationStyle_Other" : None + }, +"AsyncContentLoadedState" : { + "AsyncContentLoadedState_Beginning" : None, + "AsyncContentLoadedState_Progress" : None, + "AsyncContentLoadedState_Completed" : None + }, +"AutomationElementMode" : { + "AutomationElementMode_None" : None, + "AutomationElementMode_Full" : None + }, +"AutomationIdentifierType" : { + "AutomationIdentifierType_Property" : None, + "AutomationIdentifierType_Pattern" : None, + "AutomationIdentifierType_Event" : None, + "AutomationIdentifierType_ControlType" : None, + "AutomationIdentifierType_TextAttribute" : None + }, +"BulletStyle" : { + "BulletStyle_None" : None, + "BulletStyle_HollowRoundBullet" : None, + "BulletStyle_FilledRoundBullet" : None, + "BulletStyle_HollowSquareBullet" : None, + "BulletStyle_FilledSquareBullet" : None, + "BulletStyle_DashBullet" : None, + "BulletStyle_Other" : None + }, +"CapStyle" : { + "CapStyle_None" : None, + "CapStyle_SmallCap" : None, + "CapStyle_AllCap" : None, + "CapStyle_AllPetiteCaps" : None, + "CapStyle_PetiteCaps" : None, + "CapStyle_Unicase" : None, + "CapStyle_Titling" : None, + "Other" : None + }, +"CaretBidiMode" : { + "CaretBidiMode_LTR" : None, + "CaretBidiMode_RTL" : None + }, +"CaretPosition" : { + "CaretPosition_Unknown" : None, + "CaretPosition_EndOfLine" : None, + "CaretPosition_BeginningOfLine" : None + }, +"ConditionType" : { + "ConditionType_True" : None, + "ConditionType_False" : None, + "ConditionType_Property" : None, + "ConditionType_And" : None, + "ConditionType_Or" : None, + "ConditionType_Not" : None + }, +"DockPosition" : { + "DockPosition_Top" : None, + "DockPosition_Left" : None, + "DockPosition_Bottom" : None, + "DockPosition_Right" : None, + "DockPosition_Fill" : None, + "DockPosition_None" : None + }, +"EventArgsType" : { + "EventArgsType_Simple" : None, + "EventArgsType_PropertyChanged" : None, + "EventArgsType_StructureChanged" : None, + "EventArgsType_AsyncContentLoaded" : None, + "EventArgsType_WindowClosed" : None + }, +"ExpandCollapseState" : { + "ExpandCollapseState_Collapsed" : None, + "ExpandCollapseState_Expanded" : None, + "ExpandCollapseState_PartiallyExpanded" : None, + "ExpandCollapseState_LeafNode" : None + }, +"FlowDirections" : { + "FlowDirections_Default" : None, + "FlowDirections_RightToLeft" : None, + "FlowDirections_BottomToTop" : None, + "FlowDirections_Vertical" : None + }, +"HorizontalTextAlignmentEnum" : { + "HorizontalTextAlignment_Left" : None, + "HorizontalTextAlignment_Centered" : None, + "HorizontalTextAlignment_Right" : None, + "HorizontalTextAlignment_Justified" : None + }, +"LiveSetting" : { + "LiveSetting_Off" : None, + "LiveSetting_Polite" : None, + "LiveSetting_Assertive" : None + }, +"NavigateDirection" : { + "NavigateDirection_Parent" : None, + "NavigateDirection_NextSibling" : None, + "NavigateDirection_PreviousSibling" : None, + "NavigateDirection_FirstChild" : None, + "NavigateDirection_LastChild" : None + }, +"NormalizeState" : { + "NormalizeState_None" : None, + "NormalizeState_View" : None, + "NormalizeState_Custom" : None + }, +"OrientationType" : { + "OrientationType_None" : None, + "OrientationType_Horizontal" : None, + "OrientationType_Vertical" : None + }, +"OutlineStyles" : { + "OutlineStyles_None" : None, + "OutlineStyles_Outline" : None, + "OutlineStyles_Shadow" : None, + "OutlineStyles_Engraved" : None, + "OutlineStyles_Embossed" : None + }, +"PropertyConditionFlags" : { + "PropertyConditionFlags_None" : None, + "PropertyConditionFlags_IgnoreCase" : None + }, +"ProviderOptions" : { + "ProviderOptions_ClientSideProvider" : None, + "ProviderOptions_HasNativeIAccessible" : None, + "ProviderOptions_NonClientAreaProvider" : None, + "ProviderOptions_OverrideProvider" : None, + "ProviderOptions_ProviderOwnsSetFocus" : None, + "ProviderOptions_RefuseNonClientSupport": None, + "ProviderOptions_ServerSideProvider" : None, + "ProviderOptions_UseClientCoordinates" : None, + "ProviderOptions_UseComThreading" : None + }, +"ProviderType" : { + "ProviderType_BaseHwnd" : None, + "ProviderType_Proxy" : None, + "ProviderType_NonClientArea" : None + }, +"RowOrColumnMajor" : { + "RowOrColumnMajor_RowMajor" : None, + "RowOrColumnMajor_ColumnMajor" : None, + "RowOrColumnMajor_Indeterminate" : None + }, +"ScrollAmount" : { + "ScrollAmount_LargeDecrement" : None, + "ScrollAmount_SmallDecrement" : None, + "ScrollAmount_NoAmount" : None, + "ScrollAmount_LargeIncrement" : None, + "ScrollAmount_SmallIncrement" : None + }, +"StructureChangeType" : { + "StructureChangeType_ChildAdded" : None, + "StructureChangeType_ChildRemoved" : None, + "StructureChangeType_ChildrenInvalidated" : None, + "StructureChangeType_ChildrenBulkAdded" : None, + "StructureChangeType_ChildrenBulkRemoved" : None, + "StructureChangeType_ChildrenReordered" : None + }, +"SupportedTextSelection" : { + "SupportedTextSelection_Multiple" : None, + "SupportedTextSelection_None" : None, + "SupportedTextSelection_Single" : None + }, +"SynchronizedInputType" : { + "SynchronizedInputType_KeyUp" : None, + "SynchronizedInputType_KeyDown" : None, + "SynchronizedInputType_LeftMouseUp" : None, + "SynchronizedInputType_LeftMouseDown" : None, + "SynchronizedInputType_RightMouseUp" : None, + "SynchronizedInputType_RightMouseDown" : None + }, +"TextDecorationLineStyle" : { + "TextDecorationLineStyle_None" : None, + "TextDecorationLineStyle_Single" : None, + "TextDecorationLineStyle_WordsOnly" : None, + "TextDecorationLineStyle_Double" : None, + "TextDecorationLineStyle_Dot" : None, + "TextDecorationLineStyle_Dash" : None, + "TextDecorationLineStyle_DashDot" : None, + "TextDecorationLineStyle_DashDotDot" : None, + "TextDecorationLineStyle_Wavy" : None, + "TextDecorationLineStyle_ThickSingle" : None, + "TextDecorationLineStyle_DoubleWavy" : None, + "TextDecorationLineStyle_ThickWavy" : None, + "TextDecorationLineStyle_LongDash" : None, + "TextDecorationLineStyle_ThickDash" : None, + "TextDecorationLineStyle_ThickDashDot" : None, + "TextDecorationLineStyle_ThickDashDotDot" : None, + "TextDecorationLineStyle_ThickDot" : None, + "TextDecorationLineStyle_ThickLongDash" : None, + "TextDecorationLineStyle_Other" : None + }, +"TextEditChangeType" : { + "TextEditChangeType_None" : None, + "TextEditChangeType_AutoCorrect" : None, + "TextEditChangeType_Composition" : None, + "TextEditChangeType_CompositionFinalized" : None + }, +"TextPatternRangeEndpoint" : { + "TextPatternRangeEndpoint_Start" : None, + "TextPatternRangeEndpoint_End" : None + }, +"TextUnit" : { + "TextUnit_Character" : None, + "TextUnit_Format" : None, + "TextUnit_Word" : None, + "TextUnit_Line" : None, + "TextUnit_Paragraph" : None, + "TextUnit_Page" : None, + "TextUnit_Document" : None + }, +"ToggleState" : { + "ToggleState_Off" : None, + "ToggleState_On" : None, + "ToggleState_Indeterminate" : None + }, +"TreeScope" : { + "TreeScope_Element" : None, + "TreeScope_Children" : None, + "TreeScope_Descendants" : None, + "TreeScope_Parent" : None, + "TreeScope_Ancestors" : None, + "TreeScope_Subtree" : None + }, +"UIAutomationType" : { + "UIAutomationType_Int" : None, + "UIAutomationType_Bool" : None, + "UIAutomationType_String" : None, + "UIAutomationType_Double" : None, + "UIAutomationType_Point" : None, + "UIAutomationType_Rect" : None, + "UIAutomationType_Element" : None, + "UIAutomationType_Array" : None, + "UIAutomationType_Out" : None, + "UIAutomationType_IntArray" : None, + "UIAutomationType_BoolArray" : None, + "UIAutomationType_StringArray" : None, + "UIAutomationType_DoubleArray" : None, + "UIAutomationType_PointArray" : None, + "UIAutomationType_RectArray" : None, + "UIAutomationType_ElementArray" : None, + "UIAutomationType_OutInt" : None, + "UIAutomationType_OutBool" : None, + "UIAutomationType_OutString" : None, + "UIAutomationType_OutDouble" : None, + "UIAutomationType_OutPoint" : None, + "UIAutomationType_OutRect" : None, + "UIAutomationType_OutElement" : None, + "UIAutomationType_OutIntArray" : None, + "UIAutomationType_OutBoolArray" : None, + "UIAutomationType_OutStringArray" : None, + "UIAutomationType_OutDoubleArray" : None, + "UIAutomationType_OutPointArray" : None, + "UIAutomationType_OutRectArray" : None, + "UIAutomationType_OutElementArray" : None + }, +"WindowInteractionState" : { + "WindowInteractionState_Running" : None, + "WindowInteractionState_Closing" : None, + "WindowInteractionState_ReadyForUserInteraction" : None, + "WindowInteractionState_BlockedByModalWindow" : None, + "WindowInteractionState_NotResponding" : None + }, +"WindowVisualState" : { + "WindowVisualState_Normal" : None, + "WindowVisualState_Maximized" : None, + "WindowVisualState_Minimized" : None + }, +"ZoomUnit" : { + "ZoomUnit_NoAmount" : None, + "ZoomUnit_LargeDecrement" : None, + "ZoomUnit_SmallDecrement" : None, + "ZoomUnit_LargeIncrement" : None, + "ZoomUnit_SmallIncrement" : None + }, +} + +############################### +#UI Automation Constants +#http://msdn.microsoft.com/en-us/library/windows/desktop/ee671207(v=vs.85).aspx + +#You can check element property and pattern using inspect.exe +#http://msdn.microsoft.com/en-us/library/windows/desktop/dd318521(v=vs.85).aspx +############################### + +#Automation Element Property Identifiers +UIA_automation_element_property_identifers = ( + "AcceleratorKey", + "AccessKey", + "AriaProperties", + "AriaRole", + "AutomationId", + "BoundingRectangle", + "ClassName", + "ClickablePoint", + "ControllerFor", + "ControlType", + "Culture", + "DescribedBy", + "FlowsFrom", + "FlowsTo", + "FrameworkId", + "HasKeyboardFocus", + "HelpText", + "IsContentElement", + "IsControlElement", + "IsDataValidForForm", + "IsEnabled", + "IsKeyboardFocusable", + "IsOffscreen", + "IsPassword", + "IsPeripheral", + "IsRequiredForForm", + "ItemStatus", + "ItemType", + "LabeledBy", + "LiveSetting", + "LocalizedControlType", + "Name", + "NativeWindowHandle", + "OptimizeForVisualContent", + "Orientation", + "ProcessId", + "ProviderDescription", + "RuntimeId", + ) + +#Control Pattern Property Identifiers +UIA_control_pattern_property_identifiers = ( + "AnnotationAnnotationTypeId", + "AnnotationAnnotationTypeName", + "AnnotationAuthor", + "AnnotationDateTime", + "AnnotationTarget", + "DockDockPosition", + "DragDropEffect", + "DragDropEffects", + "DragIsGrabbed", + "DragGrabbedItems", + "DropTargetDropTargetEffect", + "DropTargetDropTargetEffects", + "ExpandCollapseExpandCollapseState", + "GridColumnCount", + "GridItemColumn", + "GridItemColumnSpan", + "GridItemContainingGrid", + "GridItemRow", + "GridItemRowSpan", + "GridRowCount", + "LegacyIAccessibleChildId", + "LegacyIAccessibleDefaultAction", + "LegacyIAccessibleDescription", + "LegacyIAccessibleHelp", + "LegacyIAccessibleKeyboardShortcut", + "LegacyIAccessibleName", + "LegacyIAccessibleRole", + "LegacyIAccessibleSelection", + "LegacyIAccessibleState", + "LegacyIAccessibleValue", + "MultipleViewCurrentView", + "MultipleViewSupportedViews", + "RangeValueIsReadOnly", + "RangeValueLargeChange", + "RangeValueMaximum", + "RangeValueMinimum", + "RangeValueSmallChange", + "RangeValueValue", + "ScrollHorizontallyScrollable", + "ScrollHorizontalScrollPercent", + "ScrollHorizontalViewSize", + "ScrollVerticallyScrollable", + "ScrollVerticalScrollPercent", + "ScrollVerticalViewSize", + "SelectionCanSelectMultiple", + "SelectionIsSelectionRequired", + "SelectionSelection", + "SelectionItemIsSelected", + "SelectionItemSelectionContainer", + "SpreadsheetItemFormula", + "SpreadsheetItemAnnotationObjects", + "SpreadsheetItemAnnotationTypes", + "StylesExtendedProperties", + "StylesFillColor", + "StylesFillPatternColor", + "StylesFillPatternStyle", + "StylesShape", + "StylesStyleId", + "StylesStyleName", + "TableColumnHeaders", + "TableItemColumnHeaderItems", + "TableRowHeaders", + "TableRowOrColumnMajor", + "TableItemRowHeaderItems", + "ToggleToggleState", + "TransformCanMove", + "TransformCanResize", + "TransformCanRotate", + "Transform2CanZoom", + "Transform2ZoomLevel", + "Transform2ZoomMaximum", + "Transform2ZoomMinimum", + "ValueIsReadOnly", + "ValueValue", + "WindowCanMaximize", + "WindowCanMinimize", + "WindowIsModal", + "WindowIsTopmost", + "WindowWindowInteractionState", + "WindowWindowVisualState", + ) + +#Pattern interfaces description +#Not use cach feature +UIA_control_pattern_interfaces = { +"AnnotationPattern" : [ + ('property', 'CurrentAnnotationTypeId', + ( 'out', 'c_int', 'retVal' )), + ('property', 'CurrentAnnotationTypeName', + ( 'out', 'BSTR', 'retVal' )), + ('property', 'CurrentAuthor', + ( 'out', 'BSTR', 'retVal' )), + ('property', 'CurrentDateTime', + ( 'out', 'BSTR', 'retVal' )), + ('property', 'CurrentTarget', + ( 'out', 'POINTER(IUIAutomationElement)', 'retVal' )), + # ('property', 'CachedAnnotationTypeId', + # ( 'out', 'c_int', 'retVal' )), + # ('property', 'CachedAnnotationTypeName', + # ( 'out', 'BSTR', 'retVal' )), + # ('property', 'CachedAuthor', + # ( 'out', 'BSTR', 'retVal' )), + # ('property', 'CachedDateTime', + # ( 'out', 'BSTR', 'retVal' )), + # ('property', 'CachedTarget', + # ( 'out', 'POINTER(IUIAutomationElement)', 'retVal' )), +], +"DockPattern" : [ + ('method', 'SetDockPosition', + ( 'in', 'DockPosition', 'dockPos' )), + ('property', 'CurrentDockPosition', + ( 'out', 'DockPosition', 'retVal' )), + # ('property', 'CachedDockPosition', + # ( 'out', 'DockPosition', 'retVal' )), +], +"DragPattern" : [ + ('property', 'CurrentIsGrabbed', + ( 'out', 'c_int', 'retVal' )), + # ('property', 'CachedIsGrabbed', + # ( 'out', 'c_int', 'retVal' )), + ('property', 'CurrentDropEffect', + ( 'out', 'BSTR', 'retVal' )), + # ('property', 'CachedDropEffect', + # ( 'out', 'BSTR', 'retVal' )), + ('property', 'CurrentDropEffects', + ( 'out', '_midlSAFEARRAY(BSTR)', 'retVal' )), + # ('property', 'CachedDropEffects', + # ( 'out', '_midlSAFEARRAY(BSTR)', 'retVal' )), + ('method', 'GetCurrentGrabbedItems', + ( 'out', 'POINTER(IUIAutomationElementArray)', 'retVal' )), + # ('method', 'GetCachedGrabbedItems', + # ( 'out', 'POINTER(IUIAutomationElementArray)', 'retVal' )), +], +"DropTargetPattern" : [ + ('property', 'CurrentDropTargetEffect', + ( 'out', 'BSTR', 'retVal' )), + # ('property', 'CachedDropTargetEffect', + # ( 'out', 'BSTR', 'retVal' )), + ('property', 'CurrentDropTargetEffects', + ( 'out', '_midlSAFEARRAY(BSTR)', 'retVal' )), + # ('property', 'CachedDropTargetEffects', + # ( 'out', '_midlSAFEARRAY(BSTR)', 'retVal' )), +], +"ExpandCollapsePattern" : [ + ('method', 'Expand'), + ('method', 'Collapse'), + ('property', 'CurrentExpandCollapseState', + ( 'out', 'POINTER(ExpandCollapseState)', 'retVal' )), + # ('property', 'CachedExpandCollapseState', + # ( 'out', 'POINTER(ExpandCollapseState)', 'retVal' )), +], +"GridItemPattern" : [ + ('property', 'CurrentContainingGrid', + ( 'out', 'POINTER(IUIAutomationElement)', 'retVal' )), + ('property', 'CurrentRow', + ( 'out', 'c_int', 'retVal' )), + ('property', 'CurrentColumn', + ( 'out', 'c_int', 'retVal' )), + ('property', 'CurrentRowSpan', + ( 'out', 'c_int', 'retVal' )), + ('property', 'CurrentColumnSpan', + ( 'out', 'c_int', 'retVal' )), + # ('property', 'CachedContainingGrid', + # ( 'out', 'POINTER(IUIAutomationElement)', 'retVal' )), + # ('property', 'CachedRow', + # ( 'out', 'c_int', 'retVal' )), + # ('property', 'CachedColumn', + # ( 'out', 'c_int', 'retVal' )), + # ('property', 'CachedRowSpan', + # ( 'out', 'c_int', 'retVal' )), + # ('property', 'CachedColumnSpan', + # ( 'out', 'c_int', 'retVal' )), +], +"GridPattern" : [ + ('method', 'GetItem', + ( 'in', 'c_int', 'row' ), + ( 'in', 'c_int', 'column' ), + ( 'out', 'POINTER(IUIAutomationElement)', 'element' )), + ('property', 'CurrentRowCount', + ( 'out', 'c_int', 'retVal' )), + ('property', 'CurrentColumnCount', + ( 'out', 'c_int', 'retVal' )), + # ('property', 'CachedRowCount', + # ( 'out', 'c_int', 'retVal' )), + # ('property', 'CachedColumnCount', + # ( 'out', 'c_int', 'retVal' )), +], +"InvokePattern" : [ + ('method', 'Invoke'), +], +"ItemContainerPattern" : [ + ('method', 'FindItemByProperty', + ( 'in', 'POINTER(IUIAutomationElement)', 'pStartAfter' ), + ( 'in', 'c_int', 'propertyId' ), + ( 'in', 'VARIANT', 'value' ), + ( 'out', 'POINTER(IUIAutomationElement)', 'pFound' )), +], +"LegacyIAccessiblePattern" : [ + ('method', 'Select', + ( 'in', 'c_int', 'flagsSelect' )), + ('method', 'DoDefaultAction'), + ('method', 'SetValue', + ( 'in', 'WSTRING', 'szValue' )), + ('property', 'CurrentChildId', + ( 'out', 'c_int', 'pRetVal' )), + ('property', 'CurrentName', + ( 'out', 'BSTR', 'pszName' )), + ('property', 'CurrentValue', + ( 'out', 'BSTR', 'pszValue' )), + ('property', 'CurrentDescription', + ( 'out', 'BSTR', 'pszDescription' )), + ('property', 'CurrentRole', + ( 'out', 'c_ulong', 'pdwRole' )), + ('property', 'CurrentState', + ( 'out', 'c_ulong', 'pdwState' )), + ('property', 'CurrentHelp', + ( 'out', 'BSTR', 'pszHelp' )), + ('property', 'CurrentKeyboardShortcut', + ( 'out', 'BSTR', 'pszKeyboardShortcut' )), + ('method', 'GetCurrentSelection', + ( 'out', 'POINTER(IUIAutomationElementArray)', 'pvarSelectedChildren' )), + ('property', 'CurrentDefaultAction', + ( 'out', 'BSTR', 'pszDefaultAction' )), + # ('property', 'CachedChildId', + # ( 'out', 'c_int', 'pRetVal' )), + # ('property', 'CachedName', + # ( 'out', 'BSTR', 'pszName' )), + # ('property', 'CachedValue', + # ( 'out', 'BSTR', 'pszValue' )), + # ('property', 'CachedDescription', + # ( 'out', 'BSTR', 'pszDescription' )), + # ('property', 'CachedRole', + # ( 'out', 'c_ulong', 'pdwRole' )), + # ('property', 'CachedState', + # ( 'out', 'c_ulong', 'pdwState' )), + # ('property', 'CachedHelp', + # ( 'out', 'BSTR', 'pszHelp' )), + # ('property', 'CachedKeyboardShortcut', + # ( 'out', 'BSTR', 'pszKeyboardShortcut' )), + # ('method', 'GetCachedSelection', + # ( 'out', 'POINTER(IUIAutomationElementArray)', 'pvarSelectedChildren' )), + # ('property', 'CachedDefaultAction', + # ( 'out', 'BSTR', 'pszDefaultAction' )), + # ('method', 'GetIAccessible', + # ( 'out', 'POINTER(IAccessible)', 'ppAccessible' )), +], +"MultipleViewPattern" : [ + ('method', 'GetViewName', + ( 'in', 'c_int', 'view' ), + ( 'out', 'BSTR', 'name' )), + ('method', 'SetCurrentView', + ( 'in', 'c_int', 'view' )), + ('property', 'CurrentCurrentView', + ( 'out', 'c_int', 'retVal' )), + ('method', 'GetCurrentSupportedViews', + ( 'out', '_midlSAFEARRAY(c_int)', 'retVal' )), + # ('property', 'CachedCurrentView', + # ( 'out', 'c_int', 'retVal' )), + # ('method', 'GetCachedSupportedViews', + # ( 'out', '_midlSAFEARRAY(c_int)', 'retVal' )), +], +"ObjectModelPattern" : [ + ('method', 'GetUnderlyingObjectModel', + ( 'out', 'POINTER(IUnknown)', 'retVal' )), +], +"RangeValuePattern" : [ + ('method', 'SetValue', + ( 'in', 'c_double', 'val' )), + ('property', 'CurrentValue', + ( 'out', 'c_double', 'retVal' )), + ('property', 'CurrentIsReadOnly', + ( 'out', 'c_int', 'retVal' )), + ('property', 'CurrentMaximum', + ( 'out', 'c_double', 'retVal' )), + ('property', 'CurrentMinimum', + ( 'out', 'c_double', 'retVal' )), + ('property', 'CurrentLargeChange', + ( 'out', 'c_double', 'retVal' )), + ('property', 'CurrentSmallChange', + ( 'out', 'c_double', 'retVal' )), + # ('property', 'CachedValue', + # ( 'out', 'c_double', 'retVal' )), + # ('property', 'CachedIsReadOnly', + # ( 'out', 'c_int', 'retVal' )), + # ('property', 'CachedMaximum', + # ( 'out', 'c_double', 'retVal' )), + # ('property', 'CachedMinimum', + # ( 'out', 'c_double', 'retVal' )), + # ('property', 'CachedLargeChange', + # ( 'out', 'c_double', 'retVal' )), + # ('property', 'CachedSmallChange', + # ( 'out', 'c_double', 'retVal' )), +], +"ScrollItemPattern" : [ + ('method', 'ScrollIntoView'), +], +"ScrollPattern" : [ + ('method', 'Scroll', + ( 'in', 'ScrollAmount', 'horizontalAmount' ), + ( 'in', 'ScrollAmount', 'verticalAmount' )), + ('method', 'SetScrollPercent', + ( 'in', 'c_double', 'horizontalPercent' ), + ( 'in', 'c_double', 'verticalPercent' )), + ('property', 'CurrentHorizontalScrollPercent', + ( 'out', 'c_double', 'retVal' )), + ('property', 'CurrentVerticalScrollPercent', + ( 'out', 'c_double', 'retVal' )), + ('property', 'CurrentHorizontalViewSize', + ( 'out', 'POINTER(c_double)', 'retVal' )), + ('property', 'CurrentVerticalViewSize', + ( 'out', 'POINTER(c_double)', 'retVal' )), + ('property', 'CurrentHorizontallyScrollable', + ( 'out', 'POINTER(c_int)', 'retVal' )), + ('property', 'CurrentVerticallyScrollable', + ( 'out', 'POINTER(c_int)', 'retVal' )), + # ('property', 'CachedHorizontalScrollPercent', + # ( 'out', 'POINTER(c_double)', 'retVal' )), + # ('property', 'CachedVerticalScrollPercent', + # ( 'out', 'POINTER(c_double)', 'retVal' )), + # ('property', 'CachedHorizontalViewSize', + # ( 'out', 'POINTER(c_double)', 'retVal' )), + # ('property', 'CachedVerticalViewSize', + # ( 'out', 'POINTER(c_double)', 'retVal' )), + # ('property', 'CachedHorizontallyScrollable', + # ( 'out', 'POINTER(c_int)', 'retVal' )), + # ('property', 'CachedVerticallyScrollable', + # ( 'out', 'POINTER(c_int)', 'retVal' )), +], +"SelectionItemPattern" : [ + ('method', 'Select'), + ('method', 'AddToSelection'), + ('method', 'RemoveFromSelection'), + ('property', 'CurrentIsSelected', + ( 'out', 'POINTER(c_int)', 'retVal' )), + ('property', 'CurrentSelectionContainer', + ( 'out', 'POINTER(POINTER(IUIAutomationElement))', 'retVal' )), + # ('property', 'CachedIsSelected', + # ( 'out', 'POINTER(c_int)', 'retVal' )), + # ('property', 'CachedSelectionContainer', + # ( 'out', 'POINTER(POINTER(IUIAutomationElement))', 'retVal' )), +], +"SelectionPattern" : [ + ('method', 'GetCurrentSelection', + ( 'out', 'POINTER(POINTER(IUIAutomationElementArray))', 'retVal' )), + ('property', 'CurrentCanSelectMultiple', + ( 'out', 'POINTER(c_int)', 'retVal' )), + ('property', 'CurrentIsSelectionRequired', + ( 'out', 'POINTER(c_int)', 'retVal' )), + # ('method', 'GetCachedSelection', + # ( 'out', 'POINTER(POINTER(IUIAutomationElementArray))', 'retVal' )), + # ('property', 'CachedCanSelectMultiple', + # ( 'out', 'POINTER(c_int)', 'retVal' )), + # ('property', 'CachedIsSelectionRequired', + # ( 'out', 'POINTER(c_int)', 'retVal' )), +], +"SpreadsheetPattern" : [ + ('method', 'GetItemByName', + ( 'in', 'BSTR', 'name' ), + ( 'out', 'POINTER(POINTER(IUIAutomationElement))', 'element' )), +], +"SpreadsheetItemPattern" : [ + ('property', 'CurrentFormula', + ( 'out', 'POINTER(BSTR)', 'retVal' )), + ('method', 'GetCurrentAnnotationObjects', + ( 'out', 'POINTER(POINTER(IUIAutomationElementArray))', 'retVal' )), + ('method', 'GetCurrentAnnotationTypes', + ( 'out', 'POINTER(_midlSAFEARRAY(c_int))', 'retVal' )), + # ('property', 'CachedFormula', + # ( 'out', 'POINTER(BSTR)', 'retVal' )), + # ('method', 'GetCachedAnnotationObjects', + # ( 'out', 'POINTER(POINTER(IUIAutomationElementArray))', 'retVal' )), + # ('method', 'GetCachedAnnotationTypes', + # ( 'out', 'POINTER(_midlSAFEARRAY(c_int))', 'retVal' )), +], +"StylesPattern" : [ + ('property', 'CurrentStyleId', + ( 'out', 'POINTER(c_int)', 'retVal' )), + ('property', 'CurrentStyleName', + ( 'out', 'POINTER(BSTR)', 'retVal' )), + ('property', 'CurrentFillColor', + ( 'out', 'POINTER(c_int)', 'retVal' )), + ('property', 'CurrentFillPatternStyle', + ( 'out', 'POINTER(BSTR)', 'retVal' )), + ('property', 'CurrentShape', + ( 'out', 'POINTER(BSTR)', 'retVal' )), + ('property', 'CurrentFillPatternColor', + ( 'out', 'POINTER(c_int)', 'retVal' )), + ('property', 'CurrentExtendedProperties', + ( 'out', 'POINTER(BSTR)', 'retVal' )), + ('method', 'GetCurrentExtendedPropertiesAsArray', + ( 'out', 'POINTER(POINTER(ExtendedProperty))', 'propertyArray' ), + ( 'out', 'POINTER(c_int)', 'propertyCount' )), + # ('property', 'CachedStyleId', + # ( 'out', 'POINTER(c_int)', 'retVal' )), + # ('property', 'CachedStyleName', + # ( 'out', 'POINTER(BSTR)', 'retVal' )), + # ('property', 'CachedFillColor', + # ( 'out', 'POINTER(c_int)', 'retVal' )), + # ('property', 'CachedFillPatternStyle', + # ( 'out', 'POINTER(BSTR)', 'retVal' )), + # ('property', 'CachedShape', + # ( 'out', 'POINTER(BSTR)', 'retVal' )), + # ('property', 'CachedFillPatternColor', + # ( 'out', 'POINTER(c_int)', 'retVal' )), + # ('property', 'CachedExtendedProperties', + # ( 'out', 'POINTER(BSTR)', 'retVal' )), + # ('method', 'GetCachedExtendedPropertiesAsArray', + # ( 'out', 'POINTER(POINTER(ExtendedProperty))', 'propertyArray' ), + # ( 'out', 'POINTER(c_int)', 'propertyCount' )), +], +"SynchronizedInputPattern" : [ + ('method', 'StartListening', + ( 'in', 'SynchronizedInputType', 'inputType' )), + ('method', 'Cancel'), +], +"TableItemPattern" : [ + ('method', 'GetCurrentRowHeaderItems', + ( 'out', 'POINTER(POINTER(IUIAutomationElementArray))', 'retVal' )), + ('method', 'GetCurrentColumnHeaderItems', + ( 'out', 'POINTER(POINTER(IUIAutomationElementArray))', 'retVal' )), + # ('method', 'GetCachedRowHeaderItems', + # ( 'out', 'POINTER(POINTER(IUIAutomationElementArray))', 'retVal' )), + # ('method', 'GetCachedColumnHeaderItems', + # ( 'out', 'POINTER(POINTER(IUIAutomationElementArray))', 'retVal' )), +], +"TablePattern" : [ + ('method', 'GetCurrentRowHeaders', + ( 'out', 'POINTER(POINTER(IUIAutomationElementArray))', 'retVal' )), + ('method', 'GetCurrentColumnHeaders', + ( 'out', 'POINTER(POINTER(IUIAutomationElementArray))', 'retVal' )), + ('property', 'CurrentRowOrColumnMajor', + ( 'out', 'POINTER(RowOrColumnMajor)', 'retVal' )), + # ('method', 'GetCachedRowHeaders', + # ( 'out', 'POINTER(POINTER(IUIAutomationElementArray))', 'retVal' )), + # ('method', 'GetCachedColumnHeaders', + # ( 'out', 'POINTER(POINTER(IUIAutomationElementArray))', 'retVal' )), + # ('property', 'CachedRowOrColumnMajor', + # ( 'out', 'POINTER(RowOrColumnMajor)', 'retVal' )), +], +"TextChildPattern" : [ + ('property', 'TextContainer', + ( 'out', 'POINTER(POINTER(IUIAutomationElement))', 'container' )), + ('property', 'TextRange', + ( 'out', 'POINTER(POINTER(IUIAutomationTextRange))', 'range' )), +], +"TextEditPattern" : None, +"TextPattern" : [ + ('method', 'RangeFromPoint', + ( 'in', 'tagPOINT', 'pt' ), + ( 'out', 'POINTER(POINTER(IUIAutomationTextRange))', 'range' )), + ('method', 'RangeFromChild', + ( 'in', 'POINTER(IUIAutomationElement)', 'child' ), + ( 'out', 'POINTER(POINTER(IUIAutomationTextRange))', 'range' )), + ('method', 'GetSelection', + ( 'out', 'POINTER(POINTER(IUIAutomationTextRangeArray))', 'ranges' )), + ('method', 'GetVisibleRanges', + ( 'out', 'POINTER(POINTER(IUIAutomationTextRangeArray))', 'ranges' )), + ('property', 'DocumentRange', + ( 'out', 'POINTER(POINTER(IUIAutomationTextRange))', 'range' )), + ('property', 'SupportedTextSelection', + ( 'out', 'POINTER(SupportedTextSelection)', 'SupportedTextSelection' )), +], +"TextPattern2" : [ + ('method', 'RangeFromAnnotation', + ( 'in', 'POINTER(IUIAutomationElement)', 'annotation' ), + ( 'out', 'POINTER(POINTER(IUIAutomationTextRange))', 'range' )), + ('method', 'GetCaretRange', + ( 'out', 'POINTER(c_int)', 'isActive' ), + ( 'out', 'POINTER(POINTER(IUIAutomationTextRange))', 'range' )), +], +"TogglePattern" : [ + ('method', 'Toggle'), + ('property', 'CurrentToggleState', + ( 'out', 'POINTER(ToggleState)', 'retVal' )), + # ('property', 'CachedToggleState', + # ( 'out', 'POINTER(ToggleState)', 'retVal' )), +], +"TransformPattern" : [ + ('method', 'Move', + ( 'in', 'c_double', 'x' ), + ( 'in', 'c_double', 'y' )), + ('method', 'Resize', + ( 'in', 'c_double', 'width' ), + ( 'in', 'c_double', 'height' )), + ('method', 'Rotate', + ( 'in', 'c_double', 'degrees' )), + ('property', 'CurrentCanMove', + ( 'out', 'POINTER(c_int)', 'retVal' )), + ('property', 'CurrentCanResize', + ( 'out', 'POINTER(c_int)', 'retVal' )), + ('property', 'CurrentCanRotate', + ( 'out', 'POINTER(c_int)', 'retVal' )), + # ('property', 'CachedCanMove', + # ( 'out', 'POINTER(c_int)', 'retVal' )), + # ('property', 'CachedCanResize', + # ( 'out', 'POINTER(c_int)', 'retVal' )), + # ('property', 'CachedCanRotate', + # ( 'out', 'POINTER(c_int)', 'retVal' )), +], +"TransformPattern2" : [ + ('method', 'Zoom', + ( 'in', 'c_double', 'Zoom' )), + ('method', 'ZoomByUnit', + ( 'in', 'ZoomUnit', 'ZoomUnit' )), + ('property', 'CurrentCanZoom', + ( 'out', 'POINTER(c_int)', 'retVal' )), + # ('property', 'CachedCanZoom', + # ( 'out', 'POINTER(c_int)', 'retVal' )), + ('property', 'CurrentZoomLevel', + ( 'out', 'POINTER(c_double)', 'retVal' )), + # ('property', 'CachedZoomLevel', + # ( 'out', 'POINTER(c_double)', 'retVal' )), + ('property', 'CurrentZoomMinimum', + ( 'out', 'POINTER(c_double)', 'retVal' )), + # ('property', 'CachedZoomMinimum', + # ( 'out', 'POINTER(c_double)', 'retVal' )), + ('property', 'CurrentZoomMaximum', + ( 'out', 'POINTER(c_double)', 'retVal' )), + # ('property', 'CachedZoomMaximum', + # ( 'out', 'POINTER(c_double)', 'retVal' )), +], +"ValuePattern" : [ + ('method', 'SetValue', + ( 'in', 'c_double', 'val' )), + ('property', 'CurrentValue', + ( 'out', 'POINTER(c_double)', 'retVal' )), + ('property', 'CurrentIsReadOnly', + ( 'out', 'POINTER(c_int)', 'retVal' )), + ('property', 'CurrentMaximum', + ( 'out', 'POINTER(c_double)', 'retVal' )), + ('property', 'CurrentMinimum', + ( 'out', 'POINTER(c_double)', 'retVal' )), + ('property', 'CurrentLargeChange', + ( 'out', 'POINTER(c_double)', 'retVal' )), + ('property', 'CurrentSmallChange', + ( 'out', 'POINTER(c_double)', 'retVal' )), + # ('property', 'CachedValue', + # ( 'out', 'POINTER(c_double)', 'retVal' )), + # ('property', 'CachedIsReadOnly', + # ( 'out', 'POINTER(c_int)', 'retVal' )), + # ('property', 'CachedMaximum', + # ( 'out', 'POINTER(c_double)', 'retVal' )), + # ('property', 'CachedMinimum', + # ( 'out', 'POINTER(c_double)', 'retVal' )), + # ('property', 'CachedLargeChange', + # ( 'out', 'POINTER(c_double)', 'retVal' )), + # ('property', 'CachedSmallChange', + # ( 'out', 'POINTER(c_double)', 'retVal' )), +], +"VirtualizedItemPattern" : [ + ('method', 'Realize'), +], +"WindowPattern" : [ + ('method', 'Close'), + ('method', 'WaitForInputIdle', + ( 'in', 'c_int', 'milliseconds' ), + ( 'out', 'POINTER(c_int)', 'success' )), + ('method', 'SetWindowVisualState', + ( 'in', 'WindowVisualState', 'state' )), + ('property', 'CurrentCanMaximize', + ( 'out', 'POINTER(c_int)', 'retVal' )), + ('property', 'CurrentCanMinimize', + ( 'out', 'POINTER(c_int)', 'retVal' )), + ('property', 'CurrentIsModal', + ( 'out', 'POINTER(c_int)', 'retVal' )), + ('property', 'CurrentIsTopmost', + ( 'out', 'POINTER(c_int)', 'retVal' )), + ('property', 'CurrentWindowVisualState', + ( 'out', 'POINTER(WindowVisualState)', 'retVal' )), + ('property', 'CurrentWindowInteractionState', + ( 'out', 'POINTER(WindowInteractionState)', 'retVal' )), + # ('property', 'CachedCanMaximize', + # ( 'out', 'POINTER(c_int)', 'retVal' )), + # ('property', 'CachedCanMinimize', + # ( 'out', 'POINTER(c_int)', 'retVal' )), + # ('property', 'CachedIsModal', + # ( 'out', 'POINTER(c_int)', 'retVal' )), + # ('property', 'CachedIsTopmost', + # ( 'out', 'POINTER(c_int)', 'retVal' )), + # ('property', 'CachedWindowVisualState', + # ( 'out', 'POINTER(WindowVisualState)', 'retVal' )), + # ('property', 'CachedWindowInteractionState', + # ( 'out', 'POINTER(WindowInteractionState)', 'retVal' )), +], +} + +#Control Pattern Identifiers +UIA_control_pattern_identifers = UIA_control_pattern_interfaces.keys() + +#Control Pattern Availability Property Identifiers +UIA_control_pattern_availability_property_identifiers = \ +[ "Is"+identifier+"Available" for identifier in UIA_control_pattern_identifers ] + +#check if enum exist in current version UIA namespace +#set the value if exist +for enum in UIA_enums.items(): + enum_name = enum[0] + enum_contents = enum[1] + #check if enum name in current UIA namespace + enum_name_type = getattr(UIA_wrapper, enum_name, None) + if enum_name_type is not ctypes.c_int: + #enum type should be c_int in UIA wrapper namespace + #skip this enum if enum type is not c_int + LOGGER.debug("Enum: %s not exist in current UIA namespace" , enum_name) + continue + + for enum_content_name in enum_contents: + enum_content_value = getattr(UIA_wrapper, enum_content_name, None) + #set the value to UIA_enums dict + UIA_enums[enum_name][enum_content_name] = enum_content_value + +#build map for property identifiers +UIA_automation_element_property_identifers_mapping = {} +for identifier in UIA_automation_element_property_identifers: + value = getattr(UIA_wrapper, "UIA_"+identifier+"PropertyId", None) + if value is None: + LOGGER.debug("Automation element property identifier: %s not exist in current UIA namespace" , identifier) + continue + UIA_automation_element_property_identifers_mapping[identifier] = value + +#build mapfor control pattern identifiers +UIA_control_pattern_property_identifiers_mapping = {} +for identifier in UIA_control_pattern_property_identifiers: + value = getattr(UIA_wrapper, "UIA_"+identifier+"PropertyId", None) + if value is None: + LOGGER.debug("Automation element control pattern property identifier: %s not exist in current UIA namespace" , identifier) + continue + UIA_control_pattern_property_identifiers_mapping[identifier] = value + +#build map for Control Pattern Availability Property Identifiers +UIA_control_pattern_availability_property_identifiers_mapping = {} +for identifier in UIA_control_pattern_availability_property_identifiers: + value = getattr(UIA_wrapper, "UIA_"+identifier+"PropertyId", None) + if value is None: + LOGGER.debug("Control pattern property identifier: %s not exist in current UIA namespace" , identifier) + continue + UIA_control_pattern_availability_property_identifiers_mapping[identifier] = value + +#build map for Control Pattern Identifiers +UIA_control_pattern_identifers_mapping = {} +for identifier in UIA_control_pattern_identifers: + identifier_value = getattr(UIA_wrapper, "UIA_"+identifier+"Id", None) + interface_value = getattr(UIA_wrapper, "IUIAutomation"+identifier, None) + if identifier_value is None: + LOGGER.debug("Control pattern identifier: %s not exist in current UIA namespace" , identifier) + continue + UIA_control_pattern_identifers_mapping[identifier] = (identifier_value, interface_value) + +def get_property_by_id(UIAElement, property_identifier): + '''get UIA element property by identifier, return None if fail + + Arguments: + UIAElement: UIA Element instance + property_identifier: property identifier + Returns: + property_value if success + None if property_identifier not valid or not supported by UIA Element + ''' + if property_identifier in UIA_automation_element_property_identifers_mapping: + property_value = UIAElement.GetCurrentPropertyValue(UIA_automation_element_property_identifers_mapping[property_identifier]) + if property_value is None: + LOGGER.debug("This property:%s is not supported by this UIAElment" , property_identifier) + return "" + return property_value + + elif property_identifier in UIA_control_pattern_availability_property_identifiers_mapping: + property_value = UIAElement.GetCurrentPropertyValue(UIA_control_pattern_availability_property_identifiers_mapping[property_identifier]) + if property_value is None: + LOGGER.debug("This property:%s is not supported by this UIAElment" , property_identifier) + return "" + return property_value + else: + LOGGER.debug("This property identifier is not support: %s, cannot get it from UIA typelib" % property_identifier) + return None + +def get_pattern_by_id(UIAElement, pattern_identifier): + '''get UIA element pattern by identifier, return None if fail + + Arguments: + UIAElement: UIA Element instance + pattern_identifier: pattern identifier + Returns: + pattern instance if success + None if pattern_identifier not valid or not supported by UIA Element + ''' + if pattern_identifier in UIA_control_pattern_identifers_mapping: + UIA_pattern_identifier = UIA_control_pattern_identifers_mapping[pattern_identifier][0] + UIA_pattern_interface = UIA_control_pattern_identifers_mapping[pattern_identifier][1] + + pattern = UIAElement.GetCurrentPatternAs(UIA_pattern_identifier, UIA_pattern_interface._iid_) + if pattern is None: + LOGGER.debug("This pattern:%s is not supported by this UIAElment" , pattern_identifier) + return None + return ctypes.POINTER(UIA_pattern_interface)(pattern) + ''' + #Use GetCurrentPatternAs to check if get pattern success + pattern = UIAElement.GetCurrentPattern(UIA_pattern_identifier).QueryInterface(UIA_pattern_interface) + return pattern + ''' + else: + LOGGER.debug("This pattern identifier is not support: %s, cannot get it from UIA typelib" , pattern_identifier) + return None diff --git a/AXUI/driver/windows_driver/UIElement.py b/AXUI/driver/windows_driver/UIElement.py index 802b586..bf9e080 100644 --- a/AXUI/driver/windows_driver/UIElement.py +++ b/AXUI/driver/windows_driver/UIElement.py @@ -2,13 +2,13 @@ from AXUI.logger import LOGGER from AXUI.exceptions import DriverException -import UIA +from . import UIA import ctypes import _ctypes -import win32 -import screenshot -import Translater +from . import win32 +from . import screenshot +from . import Translater def _unpack(flag, name, *args): return flag, name, args @@ -86,7 +86,7 @@ def __call__(self, *in_args): if args[index] in UIA.UIA_enums[expected_arg_type]: args[index] = UIA.UIA_enums[expected_arg_type][args[index]] - if args[index] not in UIA.UIA_enums[expected_arg_type].values(): + if args[index] not in list(UIA.UIA_enums[expected_arg_type].values()): LOGGER.debug("Input argument not in expected value: %s" , args[index]) return None @@ -124,7 +124,7 @@ def __init__(self, UIAElement, pattern_identifier): def __repr__(self): docstring = "" docstring += "Properties:\n" - for property_ in self.properties.items(): + for property_ in list(self.properties.items()): name = property_[0] argument = property_[1][0] value_type = argument[1] @@ -135,7 +135,7 @@ def __repr__(self): docstring += " Value:\t"+repr(value)+"\n" docstring += "\nMethods:\n" - for method_ in self.methods.items(): + for method_ in list(self.methods.items()): name = method_[0] arguments = method_[1] docstring += "#"*32+"\n" diff --git a/AXUI/driver/windows_driver/UIElement.py.bak b/AXUI/driver/windows_driver/UIElement.py.bak new file mode 100644 index 0000000..802b586 --- /dev/null +++ b/AXUI/driver/windows_driver/UIElement.py.bak @@ -0,0 +1,523 @@ + +from AXUI.logger import LOGGER +from AXUI.exceptions import DriverException + +import UIA +import ctypes +import _ctypes + +import win32 +import screenshot +import Translater + +def _unpack(flag, name, *args): + return flag, name, args + +class Method(object): + '''Wrapper class for UIA pattern method + ''' + def __init__(self, function_object, name, args_expected=None): + if args_expected is None: + args_expected = [] + + self.function_object = function_object + self.name = name + self.args = [] + self.outs = [] + for arg in args_expected: + arg_direction = arg[0] + arg_type = arg[1] + arg_name = arg[2] + if arg_direction == "in": + self.args.append([arg_type, arg_name]) + elif arg_direction == "out": + self.outs.append([arg_type, arg_name]) + else: + #skip unsupported arg_direction + raise DriverException("Unsupported arg_direction: %s" % arg_direction) + + def __repr__(self): + docstring = "Name:\t"+self.name+"\n" + argument_string = "Arguments:\n" + for argument in self.args: + argument_type = argument[0] + argument_name = argument[1] + + if argument_type == "POINTER(IUIAutomationElement)": + argument_type = "UIElement" + elif argument_type in UIA.UIA_enums: + argument_type = UIA.UIA_enums[argument_type] + + argument_string +=" Name:\t"+argument_name+"\n" + argument_string +=" Type:\t"+repr(argument_type)+"\n\n" + + return_string = "Returns:\n" + for out in self.outs: + return_name = out[1] + return_type = out[0] + return_string +=" Name:\t"+return_name+"\n" + return_string +=" Type:\t"+return_type+"\n\n" + + docstring += argument_string + docstring += return_string + + return docstring + + def __call__(self, *in_args): + ''' + For output value, use original value + For input arguments: + 1. If required argument is an enum, check if input argument fit requirement + 2. If required argument is "POINTER(IUIAutomationElement)", we accept UIElement object, + get required pointer object from UIElement, and send it to function + 3. Other, no change + ''' + args = list(in_args) + if len(self.args) != len(args): + LOGGER.warn("Input arguments number not match expected") + return None + for index, expected_arg in enumerate(self.args): + expected_arg_type = expected_arg[0] + if expected_arg_type == "POINTER(IUIAutomationElement)": + #get the UIAElment + args[index] = args[index].UIAElement + elif expected_arg_type in UIA.UIA_enums: + #enum should be an int value, if argument is a string, should translate to int + if args[index] in UIA.UIA_enums[expected_arg_type]: + args[index] = UIA.UIA_enums[expected_arg_type][args[index]] + + if args[index] not in UIA.UIA_enums[expected_arg_type].values(): + LOGGER.debug("Input argument not in expected value: %s" , args[index]) + return None + + return self.function_object(*args) + +class Pattern(object): + '''Wrapper class for UIA pattern interface + + ''' + def __init__(self, UIAElement, pattern_identifier): + self.UIAElement = UIAElement + self.pattern_object = UIA.get_pattern_by_id(UIAElement, pattern_identifier) + if self.pattern_object is None: + raise DriverException("Cannot get pattern, stop init pattern object") + self.methods = {} + self.properties = {} + interface_description = UIA.UIA_control_pattern_interfaces[pattern_identifier] + for member_description in interface_description: + flag, name, args = _unpack(*member_description) + #do a check, see if member exist in pattern object + #if not, skip this member + try: + getattr(self.pattern_object, name) + except AttributeError: + LOGGER.debug("%s not exist in Pattern:%s", name, pattern_identifier) + continue + + if flag == "method": + self.methods[name] = args + elif flag == "property": + self.properties[name] = args + else: + raise DriverException("Unrecognised flag %s" % flag) + + def __repr__(self): + docstring = "" + docstring += "Properties:\n" + for property_ in self.properties.items(): + name = property_[0] + argument = property_[1][0] + value_type = argument[1] + value = getattr(self.pattern_object, name) + docstring += "#"*32+"\n" + docstring += " Name:\t"+name+"\n" + docstring += " Value Type:\t"+value_type+"\n" + docstring += " Value:\t"+repr(value)+"\n" + + docstring += "\nMethods:\n" + for method_ in self.methods.items(): + name = method_[0] + arguments = method_[1] + docstring += "#"*32+"\n" + docstring += " Name:\t"+name+"\n" + argument_string = " Arguments:\n" + return_string = " Return:\n" + for argument in arguments: + argument_direction = argument[0] + argument_type = argument[1] + argument_name = argument[2] + + if argument_direction == "in": + if argument_type == "POINTER(IUIAutomationElement)": + argument_type = "UIElement" + elif argument_type in UIA.UIA_enums: + argument_type = UIA.UIA_enums[argument_type] + + argument_string +=" Name:\t"+argument_name+"\n" + argument_string +=" Type:\t"+repr(argument_type)+"\n\n" + elif argument_direction == "out": + return_string +=" Name:\t"+argument_name+"\n" + return_string +=" Type:\t"+argument_type+"\n\n" + + docstring += argument_string + docstring += return_string + + return docstring + + def __getattr__(self, name): + member_object = getattr(self.pattern_object, name) + if name in self.methods: + return Method(member_object, name, self.methods[name]) + elif name in self.properties: + return member_object + else: + raise AttributeError("Attribute not exist: %s" % name) + +class UIElement(object): + '''This class defines interfaces for common UI element + + Every driver (Windows, Appium, Selenium) should implement this interfaces, + provides independent interfaces for uplevel modules, so we transplant AXUI cross different platform + + Attributes: + find_element: find the first descendant element which matches parsed_identifier + find_elements: find all elements which match parsed_identifier + verify: verify current element is valid + + get_keyboard: class for keyboard related methods + get_mouse: class for mouse related methods + get_touch: class for touch related methods + + get_property: get property value for current element + get_pattern: get pattern interface for current element + ''' + def __init__(self, UIAElement): + #UIAElement is a pointer to IUIAutomation + self.UIAElement = UIAElement + + def __repr__(self): + docstring = "" + #generate UIA automation element properties + docstring += "UIA automation element properties:\n" + for identifier in UIA.UIA_automation_element_property_identifers_mapping: + value = self.get_property(identifier) + if value is not None: + docstring += " %s:\t%s\n" % (identifier, repr(value)) + + docstring += "\n" + #generate UIA control pattern availability properties + docstring += "UIA control pattern availability properties:\n" + for identifier in UIA.UIA_control_pattern_availability_property_identifiers_mapping: + value = self.get_property(identifier) + if value is not None: + docstring += " %s:\t%s\n" % (identifier, repr(value)) + + return docstring + + def _find_by_index(self, translated_identifier, scope=UIA.UIA_wrapper.TreeScope_Descendants): + if isinstance(translated_identifier, tuple) and len(translated_identifier) == 2: + identifier = translated_identifier[0] + index = translated_identifier[1] + elif isinstance(translated_identifier, int): + identifier = UIA.IUIAutomation_object.CreateTrueCondition() + index = translated_identifier + else: + LOGGER.warn("Index identifier is wrong, get %s" , repr(translated_identifier)) + return None + + target_UIAElements = self.UIAElement.FindAll(scope, identifier) + if index+1 > target_UIAElements.Length: + LOGGER.warn("Find %d matched elements, index:%d out of range", target_UIAElements.Length, index) + return None + return UIElement(target_UIAElements.GetElement(index)) + + def _find_by_UIA(self, translated_identifier, scope=UIA.UIA_wrapper.TreeScope_Descendants): + target_UIAElement = self.UIAElement.FindFirst(scope, translated_identifier) + if target_UIAElement == ctypes.POINTER(UIA.UIA_wrapper.IUIAutomationElement)(): + LOGGER.debug("Find no element matching identifier") + return None + + return UIElement(target_UIAElement) + + def find_element(self, parsed_identifier): + '''find the UI element + ''' + translated_identifier = Translater.ID_Translater(parsed_identifier).get_translated() + if translated_identifier[0] == "Coordinate": + return CoordinateElement(translated_identifier[1], self) + elif translated_identifier[0] == "Index": + return self._find_by_index(translated_identifier[1]) + elif translated_identifier[0] == "UIA": + return self._find_by_UIA(translated_identifier[1]) + + def find_elements(self, parsed_identifier): + '''find all UI elements + ''' + if parsed_identifier is None: + translated_identifier = UIA.IUIAutomation_object.CreateTrueCondition() + else: + translated_identifier = Translater.ID_Translater(parsed_identifier).get_translated() + + if translated_identifier[0] == "Coordinate" or translated_identifier[0] == "Index": + LOGGER.warn("find_elements method not support find by Coordinate or find by Index") + return [] + else: + translated_identifier = translated_identifier[1] + + scope = UIA.UIA_wrapper.TreeScope_Descendants + UIAElementArray = self.UIAElement.FindAll(scope, translated_identifier) + UIElements = [] + for i in range(UIAElementArray.Length): + UIElements.append(UIElement(UIAElementArray.GetElement(i))) + + return UIElements + + def verify(self): + '''verify UI element is still exist + ''' + flag = True + if self.UIAElement == ctypes.POINTER(UIA.UIA_wrapper.IUIAutomationElement)(): + flag = False + + try: + UIAElement = self.UIAElement.FindFirst(UIA.UIA_wrapper.TreeScope_Element, UIA.IUIAutomation_object.CreateTrueCondition()) + except _ctypes.COMError: + flag = False + UIAElement = ctypes.POINTER(UIA.UIA_wrapper.IUIAutomationElement)() + + if UIAElement == ctypes.POINTER(UIA.UIA_wrapper.IUIAutomationElement)(): + flag = False + + if not flag: + LOGGER.warn("Current UIAElement is no longer exist") + return None + + return UIElement(UIAElement) + + def get_keyboard(self): + return win32.Keyboard(self) + + def get_mouse(self): + return win32.Mouse(self) + + def get_touch(self): + return win32.Touch(self) + + def get_property(self, name): + return UIA.get_property_by_id(self.UIAElement, name) + + def get_pattern(self, name): + try: + pattern = Pattern(self.UIAElement, name) + except DriverException: + pattern = None + + return pattern + + def __getattr__(self, name): + ''' + we also support direct use name to get object + ''' + if name == "Keyboard": + return self.get_keyboard() + elif name == "Mouse": + return self.get_mouse() + elif name == "Touch": + return self.get_touch() + else: + attr = self.get_property(name) + if attr is not None: + return attr + attr = self.get_pattern(name) + if attr is not None: + return attr + raise AttributeError("Attribute not exist: %s" % name) + + #################################################### + #below methods are for keyboard and mouse operation + ##################################################### + + @property + def coordinate(self): + #BoundingRectangle property value is (left, top, long, high) + #CurrentBoundingRectangle value is (left, top, right, bottom) + #use CurrentBoundingRectangle to be conpatible with Inspect.exe + value = self.UIAElement.CurrentBoundingRectangle + return value.left, value.top, value.right, value.bottom + + def set_focus(self): + '''set foucs this element + + Will bring this element to the front, used by Keyboard, Mouse, Touch + + Arguments: + Returns: + ''' + try: + self.UIAElement.SetFocus() + except _ctypes.COMError: + LOGGER.warn("SetFocus fail on current element, maybe due to this element not support SetFocus") + #self.parent.SetFocus() + + def get_clickable_point(self): + '''Retrieves a point on the element that can be clicked. + + Arguments: + Returns: + (x, y) coordinate if can get clickable point + None if cannot get clickable point + ''' + point, flag = self.UIAElement.GetClickablePoint() + if flag: + return point.x, point.y + else: + #use coordinate + x = (self.coordinate[0]+self.coordinate[2])/2 + y = (self.coordinate[1]+self.coordinate[3])/2 + return x, y + +class Root(UIElement): + ''' + root is the entry point to interact with UI + like desktop of windows UIA, web browser of web driver API + + This class defines interfaces for root element + + Every driver (Windows, Appium, Selenium) should implement this interfaces, + provides independent interfaces for uplevel modules, so we transplant AXUI cross different platform + + Attributes: + start: start root element + stop: stop root element + screenshot: take a screen shot for root element + + find_element: find the first descendant element which matches parsed_identifier + find_elements: find all elements which match parsed_identifier + verify: verify current element is valid + + get_keyboard: class for keyboard related methods + get_mouse: class for mouse related methods + get_touch: class for touch related methods + + get_property: get property value for current element + get_pattern: get pattern interface for current element + ''' + def __init__(self): + self.UIAElement = None + + def start(self, **kwargs): + self.UIAElement = UIA.IUIAutomation_object.GetRootElement() + + def stop(self, **kwargs): + self.UIAElement = None + + def screenshot(self, filename): + return screenshot.screenshot(filename) + + def find_element(self, parsed_identifier): + '''find the UI element + root find should only find in the first level to avoid search in all UI + ''' + translated_identifier = Translater.ID_Translater(parsed_identifier).get_translated() + if translated_identifier[0] == "Coordinate": + return CoordinateElement(translated_identifier[1], self) + elif translated_identifier[0] == "Index": + return self._find_by_index(translated_identifier[1], scope=UIA.UIA_wrapper.TreeScope_Children) + elif translated_identifier[0] == "UIA": + return self._find_by_UIA(translated_identifier[1], scope=UIA.UIA_wrapper.TreeScope_Children) + + def find_elements(self, parsed_identifier): + '''find all matched element + root find should only find in the first level to avoid search in all UI + ''' + if parsed_identifier is None: + translated_identifier = UIA.IUIAutomation_object.CreateTrueCondition() + else: + translated_identifier = Translater.ID_Translater(parsed_identifier).get_translated() + if translated_identifier[0] == "Coordinate" or translated_identifier[0] == "Index": + LOGGER.warn("find_elements method not support find by Coordinate or find by Index") + return [] + else: + translated_identifier = translated_identifier[1] + + scope = UIA.UIA_wrapper.TreeScope_Children + UIAElementArray = self.UIAElement.FindAll(scope, translated_identifier) + UIElements = [] + for i in range(UIAElementArray.Length): + UIElements.append(UIElement(UIAElementArray.GetElement(i))) + return UIElements + + def verify(self): + '''verify UI element is still exist + for root element just check if UIAElement is initialized + ''' + if self.UIAElement is None: + return None + else: + return self + +class CoordinateElement(UIElement): + ''' + coordinate element is for coordinate identifier + functions are limited, only support keyboard, mouse and touch operation + ''' + def __init__(self, coordinate, parent_element): + #coordinate should be like: + #"(left, top, right, bottom)", "[left, top, right, bottom]", "left, top, right, bottom" + coordinate_list = coordinate.strip("(").strip(")").strip("[").strip("]").split(",") + try: + left, top, right, bottom = [int(value) for value in coordinate_list] + if left > right or top > bottom: + raise ValueError() + except ValueError: + raise ValueError("Coordinate should contain 4 digitals, get:%s" % repr(coordinate)) + + self.coordinate = (left, top, right, bottom) + self.parent_element = parent_element + + def __repr__(self): + docstring = "Coordinate element for coordinate: %s" % repr(self.coordinate) + return docstring + + def find_element(self, parsed_identifier): + #TODO maybe we should let coordinate element have children + raise DriverException("coordinate element should not have children") + + def find_elements(self, parsed_identifier): + #TODO maybe we should let coordinate element have children + raise DriverException("coordinate element should not have children") + + def verify(self): + return self + + def set_focus(self): + '''set foucs this element + + Will bring this element to the front, used by Keyboard, Mouse, Touch + + Arguments: + Returns: + ''' + #usually, a coordinate element will be set focus if its parent is set focus + return self.parent_element.SetFocus() + + def get_clickable_point(self): + '''Retrieves a point on the element that can be clicked. + + Arguments: + Returns: + (x, y) coordinate if can get clickable point + None if cannot get clickable point + ''' + x = (self.coordinate[0]+self.coordinate[2])/2 + y = (self.coordinate[1]+self.coordinate[3])/2 + return x, y + + def get_property(self, name): + raise DriverException("coordinate element don't support property") + + def get_pattern(self, name): + raise DriverException("coordinate element don't support pattern") + + def __getattr__(self, name): + raise AttributeError("Attribute not exist for coordinate element: %s" % name) diff --git a/AXUI/driver/windows_driver/__init__.py b/AXUI/driver/windows_driver/__init__.py index dabd53b..6a3efb1 100644 --- a/AXUI/driver/windows_driver/__init__.py +++ b/AXUI/driver/windows_driver/__init__.py @@ -13,4 +13,4 @@ shcore = ctypes.windll.shcore shcore.SetProcessDpiAwareness(2) -from UIElement import UIElement, Root +from .UIElement import UIElement, Root diff --git a/AXUI/driver/windows_driver/__init__.py.bak b/AXUI/driver/windows_driver/__init__.py.bak new file mode 100644 index 0000000..dabd53b --- /dev/null +++ b/AXUI/driver/windows_driver/__init__.py.bak @@ -0,0 +1,16 @@ +#set DPI awareness for win8.1 and win10 +from AXUI.logger import LOGGER +import subprocess +p = subprocess.Popen("wmic path win32_operatingsystem get version", stdout=subprocess.PIPE) +(stdout, stderr) = p.communicate() +windows_version = stdout.split()[1] +LOGGER.debug("Windows verison: %s" , windows_version) +#need set DPI awareness for win8.1 and win10 +if (int(windows_version.split(".")[0]) >= 6 and int(windows_version.split(".")[1]) >= 3) \ + or int(windows_version.split(".")[0]) >= 10: + LOGGER.debug("Set DPI awareness for windows verison: %s" , windows_version) + import ctypes + shcore = ctypes.windll.shcore + shcore.SetProcessDpiAwareness(2) + +from UIElement import UIElement, Root diff --git a/AXUI/driver/windows_driver/win32/__init__.py b/AXUI/driver/windows_driver/win32/__init__.py index 82635a9..2e20f9f 100644 --- a/AXUI/driver/windows_driver/win32/__init__.py +++ b/AXUI/driver/windows_driver/win32/__init__.py @@ -1,2 +1,2 @@ -from keyboard import Keyboard -from mouse import Mouse \ No newline at end of file +from .keyboard import Keyboard +from .mouse import Mouse \ No newline at end of file diff --git a/AXUI/driver/windows_driver/win32/__init__.py.bak b/AXUI/driver/windows_driver/win32/__init__.py.bak new file mode 100644 index 0000000..82635a9 --- /dev/null +++ b/AXUI/driver/windows_driver/win32/__init__.py.bak @@ -0,0 +1,2 @@ +from keyboard import Keyboard +from mouse import Mouse \ No newline at end of file diff --git a/AXUI/driver/windows_driver/win32/keyboard.py b/AXUI/driver/windows_driver/win32/keyboard.py index a6d349a..2977175 100644 --- a/AXUI/driver/windows_driver/win32/keyboard.py +++ b/AXUI/driver/windows_driver/win32/keyboard.py @@ -273,7 +273,7 @@ class INPUT(ctypes.Structure): 'ZOOM': 251, } # reverse the CODES dict to make it easy to look up a particular code name -CODE_NAMES = dict((entry[1], entry[0]) for entry in CODES.items()) +CODE_NAMES = dict((entry[1], entry[0]) for entry in list(CODES.items())) # modifier keys MODIFIERS = { @@ -300,8 +300,8 @@ class KeyAction(object): def __init__(self, key, down = True, up = True): self.key = key - if isinstance(self.key, basestring): - self.key = unicode(key) + if isinstance(self.key, str): + self.key = str(key) self.down = down self.up = up @@ -541,7 +541,7 @@ def parse_keys(string, c = string[index] index += 1 # check if one of CTRL, SHIFT, ALT has been pressed - if c in MODIFIERS.keys(): + if c in list(MODIFIERS.keys()): modifier = MODIFIERS[c] # remember that we are currently modified modifiers.append(modifier) diff --git a/AXUI/driver/windows_driver/win32/keyboard.py.bak b/AXUI/driver/windows_driver/win32/keyboard.py.bak new file mode 100644 index 0000000..a6d349a --- /dev/null +++ b/AXUI/driver/windows_driver/win32/keyboard.py.bak @@ -0,0 +1,730 @@ +''' +Keyboard module, porting from pywinauto project +code.google.com/p/pywinauto +''' + +""" +Check that SendInput can work the way we want it to + +The tips and tricks at http://www.pinvoke.net/default.aspx/user32.sendinput +is useful! + +""" +import re +import time +import ctypes + +#pylint: disable-msg=R0903 + +MapVirtualKey = ctypes.windll.user32.MapVirtualKeyW +SendInput = ctypes.windll.user32.SendInput +VkKeyScan = ctypes.windll.user32.VkKeyScanW +VkKeyScan.restype = ctypes.c_short +VkKeyScan.argtypes = [ctypes.c_wchar] + +DWORD = ctypes.c_ulong +LONG = ctypes.c_long +WORD = ctypes.c_ushort + + +# C:/PROGRA~1/MICROS~4/VC98/Include/winuser.h 4283 +class MOUSEINPUT(ctypes.Structure): + "Needed for complete definition of INPUT structure - not used" + _pack_ = 2 + _fields_ = [ + # C:/PROGRA~1/MICROS~4/VC98/Include/winuser.h 4283 + ('dx', LONG), + ('dy', LONG), + ('mouseData', DWORD), + ('dwFlags', DWORD), + ('time', DWORD), + ('dwExtraInfo', DWORD), + ] +assert ctypes.sizeof(MOUSEINPUT) == 24, ctypes.sizeof(MOUSEINPUT) +assert ctypes.alignment(MOUSEINPUT) == 2, ctypes.alignment(MOUSEINPUT) + + +# C:/PROGRA~1/MICROS~4/VC98/Include/winuser.h 4292 +class KEYBDINPUT(ctypes.Structure): + "A particular keyboard event" + _pack_ = 2 + _fields_ = [ + # C:/PROGRA~1/MICROS~4/VC98/Include/winuser.h 4292 + ('wVk', WORD), + ('wScan', WORD), + ('dwFlags', DWORD), + ('time', DWORD), + ('dwExtraInfo', DWORD), + ] +assert ctypes.sizeof(KEYBDINPUT) == 16, ctypes.sizeof(KEYBDINPUT) +assert ctypes.alignment(KEYBDINPUT) == 2, ctypes.alignment(KEYBDINPUT) + + +class HARDWAREINPUT(ctypes.Structure): + "Needed for complete definition of INPUT structure - not used" + _pack_ = 2 + _fields_ = [ + # C:/PROGRA~1/MICROS~4/VC98/Include/winuser.h 4300 + ('uMsg', DWORD), + ('wParamL', WORD), + ('wParamH', WORD), + ] +assert ctypes.sizeof(HARDWAREINPUT) == 8, ctypes.sizeof(HARDWAREINPUT) +assert ctypes.alignment(HARDWAREINPUT) == 2, ctypes.alignment(HARDWAREINPUT) + + +# C:/PROGRA~1/MICROS~4/VC98/Include/winuser.h 4314 +class UNION_INPUT_STRUCTS(ctypes.Union): + "The C Union type representing a single Event of any type" + _fields_ = [ + # C:/PROGRA~1/MICROS~4/VC98/Include/winuser.h 4314 + ('mi', MOUSEINPUT), + ('ki', KEYBDINPUT), + ('hi', HARDWAREINPUT), + ] +assert ctypes.sizeof(UNION_INPUT_STRUCTS) == 24, \ + ctypes.sizeof(UNION_INPUT_STRUCTS) +assert ctypes.alignment(UNION_INPUT_STRUCTS) == 2, \ + ctypes.alignment(UNION_INPUT_STRUCTS) + + +# C:/PROGRA~1/MICROS~4/VC98/Include/winuser.h 4310 +class INPUT(ctypes.Structure): + "See: http://msdn.microsoft.com/en-us/library/ms646270%28VS.85%29.aspx" + _pack_ = 2 + _fields_ = [ + # C:/PROGRA~1/MICROS~4/VC98/Include/winuser.h 4310 + ('type', DWORD), + # Unnamed field renamed to '_' + ('_', UNION_INPUT_STRUCTS), + ] +assert ctypes.sizeof(INPUT) == 28, ctypes.sizeof(INPUT) +assert ctypes.alignment(INPUT) == 2, ctypes.alignment(INPUT) + + +INPUT_KEYBOARD = 1 +KEYEVENTF_EXTENDEDKEY = 1 +KEYEVENTF_KEYUP = 2 +KEYEVENTF_UNICODE = 4 +KEYEVENTF_SCANCODE = 8 +VK_SHIFT = 16 +VK_CONTROL = 17 +VK_MENU = 18 +VK_LWIN = 91 + +# 'codes' recognized as {CODE( repeat)?} +CODES = { + 'BACK': 8, + 'BACKSPACE':8, + 'BKSP': 8, + 'BREAK': 3, + 'BS': 8, + 'CAP': 20, + 'CAPSLOCK': 20, + 'DEL': 46, + 'DELETE': 46, + 'DOWN': 40, + 'END': 35, + 'ENTER': 13, + 'ESC': 27, + 'F1': 112, + 'F2': 113, + 'F3': 114, + 'F4': 115, + 'F5': 116, + 'F6': 117, + 'F7': 118, + 'F8': 119, + 'F9': 120, + 'F10': 121, + 'F11': 122, + 'F12': 123, + 'F13': 124, + 'F14': 125, + 'F15': 126, + 'F16': 127, + 'F17': 128, + 'F18': 129, + 'F19': 130, + 'F20': 131, + 'F21': 132, + 'F22': 133, + 'F23': 134, + 'F24': 135, + 'HELP': 47, + 'HOME': 36, + 'INS': 45, + 'INSERT': 45, + 'LEFT': 37, + 'LWIN': 91, + 'NUMLOCK': 144, + 'PGDN': 34, + 'PGUP': 33, + 'PRTSC': 44, + 'RIGHT': 39, + 'RMENU': 165, + 'RWIN': 92, + 'SCROLLLOCK':145, + 'SPACE': 32, + 'TAB': 9, + 'UP': 38, + + 'VK_ACCEPT': 30, + 'VK_ADD': 107, + 'VK_APPS': 93, + 'VK_ATTN': 246, + 'VK_BACK': 8, + 'VK_CANCEL': 3, + 'VK_CAPITAL': 20, + 'VK_CLEAR': 12, + 'VK_CONTROL': 17, + 'VK_CONVERT': 28, + 'VK_CRSEL': 247, + 'VK_DECIMAL': 110, + 'VK_DELETE': 46, + 'VK_DIVIDE': 111, + 'VK_DOWN': 40, + 'VK_END': 35, + 'VK_EREOF': 249, + 'VK_ESCAPE': 27, + 'VK_EXECUTE': 43, + 'VK_EXSEL': 248, + 'VK_F1': 112, + 'VK_F2': 113, + 'VK_F3': 114, + 'VK_F4': 115, + 'VK_F5': 116, + 'VK_F6': 117, + 'VK_F7': 118, + 'VK_F8': 119, + 'VK_F9': 120, + 'VK_F10': 121, + 'VK_F11': 122, + 'VK_F12': 123, + 'VK_F13': 124, + 'VK_F14': 125, + 'VK_F15': 126, + 'VK_F16': 127, + 'VK_F17': 128, + 'VK_F18': 129, + 'VK_F19': 130, + 'VK_F20': 131, + 'VK_F21': 132, + 'VK_F22': 133, + 'VK_F23': 134, + 'VK_F24': 135, + 'VK_FINAL': 24, + 'VK_HANGEUL': 21, + 'VK_HANGUL': 21, + 'VK_HANJA': 25, + 'VK_HELP': 47, + 'VK_HOME': 36, + 'VK_INSERT': 45, + 'VK_JUNJA': 23, + 'VK_KANA': 21, + 'VK_KANJI': 25, + 'VK_LBUTTON': 1, + 'VK_LCONTROL':162, + 'VK_LEFT': 37, + 'VK_LMENU': 164, + 'VK_LSHIFT': 160, + 'VK_LWIN': 91, + 'VK_MBUTTON': 4, + 'VK_MENU': 18, + 'VK_MODECHANGE': 31, + 'VK_MULTIPLY': 106, + 'VK_NEXT': 34, + 'VK_NONAME': 252, + 'VK_NONCONVERT': 29, + 'VK_NUMLOCK': 144, + 'VK_NUMPAD0': 96, + 'VK_NUMPAD1': 97, + 'VK_NUMPAD2': 98, + 'VK_NUMPAD3': 99, + 'VK_NUMPAD4': 100, + 'VK_NUMPAD5': 101, + 'VK_NUMPAD6': 102, + 'VK_NUMPAD7': 103, + 'VK_NUMPAD8': 104, + 'VK_NUMPAD9': 105, + 'VK_OEM_CLEAR': 254, + 'VK_PA1': 253, + 'VK_PAUSE': 19, + 'VK_PLAY': 250, + 'VK_PRINT': 42, + 'VK_PRIOR': 33, + 'VK_PROCESSKEY': 229, + 'VK_RBUTTON': 2, + 'VK_RCONTROL': 163, + 'VK_RETURN': 13, + 'VK_RIGHT': 39, + 'VK_RMENU': 165, + 'VK_RSHIFT': 161, + 'VK_RWIN': 92, + 'VK_SCROLL': 145, + 'VK_SELECT': 41, + 'VK_SEPARATOR': 108, + 'VK_SHIFT': 16, + 'VK_SNAPSHOT': 44, + 'VK_SPACE': 32, + 'VK_SUBTRACT': 109, + 'VK_TAB': 9, + 'VK_UP': 38, + 'ZOOM': 251, +} +# reverse the CODES dict to make it easy to look up a particular code name +CODE_NAMES = dict((entry[1], entry[0]) for entry in CODES.items()) + +# modifier keys +MODIFIERS = { + '+': VK_SHIFT, + '^': VK_CONTROL, + '%': VK_MENU, + '~': VK_LWIN, +} + + +class KeySequenceError(Exception): + """Exception raised when a key sequence string has a syntax error""" + + def __str__(self): + return ' '.join(self.args) + + +class KeyAction(object): + """Class that represents a single 'keyboard' action + + It represents either a PAUSE action (not really keyboard) or a keyboard + action (press or release or both) of a particular key. + """ + + def __init__(self, key, down = True, up = True): + self.key = key + if isinstance(self.key, basestring): + self.key = unicode(key) + self.down = down + self.up = up + + def _get_key_info(self): + """Return virtual_key, scan_code, and flags for the action + + This is one of the methods that will be overridden by sub classes""" + return 0, ord(self.key), KEYEVENTF_UNICODE + + def GetInput(self): + "Build the INPUT structure for the action" + actions = 1 + # if both up and down + if self.up and self.down: + actions = 2 + + inputs = (INPUT * actions)() + + vk, scan, flags = self._get_key_info() + + for inp in inputs: + inp.type = INPUT_KEYBOARD + + inp._.ki.wVk = vk + inp._.ki.wScan = scan + inp._.ki.dwFlags |= flags + + # if we are releasing - then let it up + if self.up: + inputs[-1]._.ki.dwFlags |= KEYEVENTF_KEYUP + + return inputs + + def Run(self): + "Execute the action" + inputs = self.GetInput() + return SendInput( + len(inputs), + ctypes.byref(inputs), + ctypes.sizeof(INPUT)) + + def _get_down_up_string(self): + """Return a string that will show whether the string is up or down + + return 'down' if the key is a press only + return 'up' if the key is up only + return '' if the key is up & down (as default) + """ + down_up = "" + if not (self.down and self.up): + if self.down: + down_up = "down" + elif self.up: + down_up = "up" + return down_up + + def key_description(self): + "Return a description of the key" + vk, scan, flags = self._get_key_info() + desc = '' + if vk: + if vk in CODE_NAMES: + desc = CODE_NAMES[vk] + else: + desc = "VK %d"% vk + else: + desc = "%s"% self.key + + return desc + + def __str__(self): + parts = [] + parts.append(self.key_description()) + up_down = self._get_down_up_string() + if up_down: + parts.append(up_down) + + return "<%s>"% (" ".join(parts)) + __repr__ = __str__ + + +class VirtualKeyAction(KeyAction): + """Represents a virtual key action e.g. F9 DOWN, etc + + Overrides necessary methods of KeyAction""" + + def _get_key_info(self): + "Virtual keys have extended flag set" + + # copied more or less verbatim from + # http://www.pinvoke.net/default.aspx/user32.sendinput + if ( + (self.key >= 33 and self.key <= 46) or + (self.key >= 91 and self.key <= 93) ): + flags = KEYEVENTF_EXTENDEDKEY; + else: + flags = 0 + # This works for %{F4} - ALT + F4 + #return self.key, 0, 0 + + # this works for Tic Tac Toe i.e. +{RIGHT} SHIFT + RIGHT + return self.key, MapVirtualKey(self.key, 0), flags + + +class EscapedKeyAction(KeyAction): + """Represents an escaped key action e.g. F9 DOWN, etc + + Overrides necessary methods of KeyAction""" + + def _get_key_info(self): + """EscapedKeyAction doesn't send it as Unicode and the vk and + scan code are generated differently""" + vkey_scan = LoByte(VkKeyScan(self.key)) + + return (vkey_scan, MapVirtualKey(vkey_scan, 0), 0) + + def key_description(self): + "Return a description of the key" + + return "KEsc %s"% self.key + + +class PauseAction(KeyAction): + "Represents a pause action" + + def __init__(self, how_long): + self.how_long = how_long + + def Run(self): + "Pause for the lenght of time specified" + time.sleep(self.how_long) + + def __str__(self): + return ""% (self.how_long) + __repr__ = __str__ + + #def GetInput(self): + # print `self.key` + # keys = KeyAction.GetInput(self) + # + # shift_state = HiByte(VkKeyScan(self.key)) + # + # shift_down = shift_state & 0x100 # 1st bit + # ctrl_down = shift_state & 0x80 # 2nd bit + # alt_down = shift_state & 0x40 # 3rd bit + # + # print bin(shift_state), shift_down, ctrl_down, alt_down + # + # print keys + # keys = [k for k in keys] + # + # modifiers = [] + # if shift_down: + # keys[0:0] = VirtualKeyAction(VK_SHIFT, up = False).GetInput() + # keys.append(VirtualKeyAction(VK_SHIFT, down = False).GetInput()) + # if ctrl_down: + # keys[0:0] = VirtualKeyAction(VK_CONTROL, up = False).GetInput() + # keys.append(VirtualKeyAction(VK_CONTROL, down = False).GetInput()) + # if alt_down: + # keys[0:0] = VirtualKeyAction(VK_ALT, up = False).GetInput() + # keys.append(VirtualKeyAction(VK_ALT, down = False).GetInput()) + # + # print keys + # new_keys = (INPUT * len(keys)) () + # + # for i, k in enumerate(keys): + # if hasattr(k, 'type'): + # new_keys[i] = k + # else: + # for sub_key in k: + # new_keys[i] = sub_key + # + # return new_keys + # + +def handle_code(code): + "Handle a key or sequence of keys in braces" + + code_keys = [] + # it is a known code (e.g. {DOWN}, {ENTER}, etc) + if code in CODES: + code_keys.append(VirtualKeyAction(CODES[code])) + + # it is an escaped modifier e.g. {%}, {^}, {+}, {~} + elif len(code) == 1: + code_keys.append(KeyAction(code)) + + # it is a repetition or a pause {DOWN 5}, {PAUSE 1.3} + elif ' ' in code: + to_repeat, count = code.rsplit(None, 1) + if to_repeat == "PAUSE": + try: + pause_time = float(count) + except ValueError: + raise KeySequenceError('invalid pause time %s'% count) + code_keys.append(PauseAction(pause_time)) + + else: + try: + count = int(count) + except ValueError: + raise KeySequenceError( + 'invalid repetition count %s'% count) + + # If the value in to_repeat is a VK e.g. DOWN + # we need to add the code repeated + if to_repeat in CODES: + code_keys.extend( + [VirtualKeyAction(CODES[to_repeat])] * count) + # otherwise parse the keys and we get back a KeyAction + else: + to_repeat = parse_keys(to_repeat) + if isinstance(to_repeat, list): + keys = to_repeat * count + else: + keys = [to_repeat] * count + code_keys.extend(keys) + else: + raise RuntimeError("Unknown code: %s"% code) + + return code_keys + + +def parse_keys(string, + with_spaces = False, + with_tabs = False, + with_newlines = False, + modifiers = None): + "Return the parsed keys" + + keys = [] + if not modifiers: + modifiers = [] + index = 0 + while index < len(string): + + c = string[index] + index += 1 + # check if one of CTRL, SHIFT, ALT has been pressed + if c in MODIFIERS.keys(): + modifier = MODIFIERS[c] + # remember that we are currently modified + modifiers.append(modifier) + # hold down the modifier key + keys.append(VirtualKeyAction(modifier, up = False)) + continue + + # Apply modifiers over a bunch of characters (not just one!) + elif c == "(": + # find the end of the bracketed text + end_pos = string.find(")", index) + if end_pos == -1: + raise KeySequenceError('`)` not found') + keys.extend( + parse_keys(string[index:end_pos], modifiers = modifiers)) + index = end_pos + 1 + + # Escape or named key + elif c == "{": + end_pos = string.find("}", index) + if end_pos == -1: + raise KeySequenceError('`}` not found') + + code = string[index:end_pos] + index = end_pos + 1 + keys.extend(handle_code(code)) + + # unmatched ")" + elif c == ')': + raise KeySequenceError('`)` should be preceeded by `(`') + + # unmatched "}" + elif c == '}': + raise KeySequenceError('`}` should be preceeded by `{`') + + # so it is a normal character + else: + # don't output white space unless flags to output have been set + if (c == ' ' and not with_spaces or + c == '\t' and not with_tabs or + c == '\n' and not with_newlines): + continue + + # output newline + if c in ('\n'): + keys.append(VirtualKeyAction(CODES["ENTER"])) + + # safest are the virtual keys - so if our key is a virtual key + # use a VirtualKeyAction + #if ord(c) in CODE_NAMES: + # keys.append(VirtualKeyAction(ord(c))) + + elif modifiers: + keys.append(EscapedKeyAction(c)) + + else: + keys.append(KeyAction(c)) + + # as we have handled the text - release the modifiers + while modifiers: + keys.append(VirtualKeyAction(modifiers.pop(), down = False)) + + # just in case there were any modifiers left pressed - release them + while modifiers: + keys.append(VirtualKeyAction(modifiers.pop(), down = False)) + + return keys + +def LoByte(val): + "Return the low byte of the value" + return val & 0xff + +def HiByte(val): + "Return the high byte of the value" + return (val & 0xff00) >> 8 + +from AXUI.logger import LOGGER +class Keyboard(object): + '''class for win32 keyboard input + + Attributes: + Input: Input key to target UI element + + ''' + + #for key combinations + key_combinations = { + "CTRL" : "^", + "SHIFT" : "+", + "MENU" : "%", + "LWIN" : "~" + } + + def __init__(self, UIElement): + self.UIElement = UIElement + + def __repr__(self): + docstring = ''' +Function: + Name: + Input + Description:''' + docstring +=self.Input.__doc__ + return docstring + + def input(self, *values): + '''take a string, translate to win32 event, and input to system + Arguments: + string: a string represent the keys input + pause: global pause time during each key input, default to 0.05s + Returns: + + Keyboard input string introduce: + (1) For normal charactors like [0~9][a~z][A~Z], input directly + (2) For special charactors like "space", "tab", "newline", "F1~F12" + You use {key_name} to replace them + Here are the support key list: + backspace : {BACK}, {BACKSPACE}, {BKSP}, {BS} + break : {BREAK} + capslock : {CAP}, {CAPSLOCK} + delete : {DEL}, {DELETE} + down : {DOWN} + end : {END} + enter : {ENTER} + ESC : {ESC} + F1~F24 : {F1}~{F24} + help : {HELP} + home : {HOME} + insert : {INS}, {INSERT} + left : {LEFT} + left windows : {LWIN} + number lock : {NUMLOCK} + page down : {PGDN} + page up : {PGUP} + print screen : {PRTSC} + right : {RIGHT} + right menu : {RMENU} + right windows : {RWIN} + scroll lock : {SCROLLLOCK} + space : {SPACE} + tab : {TAB} + up : {UP} + (3) For key combinations + ctrl+X : {CTRL+X} + shift+X : {SHIFT+X} + menu+X : {MENU+X} + windows key+X : {LWIN+X} + ctrl+shift+X : {CTRL+SHIFT+X} + ctrl+(XYZ) : {CTRL+XYZ} + ''' + #set focus before input keys + self.UIElement.set_focus() + + #translate arguments to a string + LOGGER.debug("Before translate, value: %s" , repr(values)) + string = "" + for value in values: + #value should only allow to be string or int + if isinstance(value, int): + translated_value = str(value) + string += translated_value + elif isinstance(value, str): + if re.match("^{\+*}$", value) != None: + #for key combinations, we need to do some translation + #like "{CTRL+A}" to "^A", "{CTRL+SHIFT+B}" to "^+B" + keys = value.lstrip("{").rstrip("}").split("+") + for key in keys: + if key.upper() in self.key_combinations: + translated_value += self.key_combinations[key.upper()] + else: + translated_value += "("+key+")" + else: + translated_value = value + + string += translated_value + else: + LOGGER.warning("keyboard input method arguments can only be string or int value, other value will be skipped") + + LOGGER.debug("Translated string: %s" , string) + + keys = parse_keys(string) + LOGGER.debug("Keyboard input string: %s, parsed keys: %s" , string, repr(keys)) + for k in keys: + k.Run() + time.sleep(0.05) + diff --git a/AXUI/driver/windows_driver/win32/mouse.py b/AXUI/driver/windows_driver/win32/mouse.py index b763024..485d270 100644 --- a/AXUI/driver/windows_driver/win32/mouse.py +++ b/AXUI/driver/windows_driver/win32/mouse.py @@ -334,16 +334,16 @@ def move(self, abs_source_coords, abs_dest_coords): sample_size = x_sample_size > y_sample_size and x_sample_size or y_sample_size #build population if abs_source_coords[0] < abs_dest_coords[0]: - x_population = range(abs_source_coords[0], abs_dest_coords[0]) + x_population = list(range(abs_source_coords[0], abs_dest_coords[0])) else: - x_population = range(abs_dest_coords[0], abs_source_coords[0]) + x_population = list(range(abs_dest_coords[0], abs_source_coords[0])) while len(x_population) y_sample_size and x_sample_size or y_sample_size + #build population + if abs_source_coords[0] < abs_dest_coords[0]: + x_population = range(abs_source_coords[0], abs_dest_coords[0]) + else: + x_population = range(abs_dest_coords[0], abs_source_coords[0]) + while len(x_population) v documentation". +#html_title = None + +# A shorter title for the navigation bar. Default is the same as html_title. +#html_short_title = None + +# The name of an image file (relative to this directory) to place at the top +# of the sidebar. +#html_logo = None + +# The name of an image file (within the static path) to use as favicon of the +# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 +# pixels large. +#html_favicon = None + +# Add any paths that contain custom static files (such as style sheets) here, +# relative to this directory. They are copied after the builtin static files, +# so a file named "default.css" will overwrite the builtin "default.css". +html_static_path = ['_static'] + +# Add any extra paths that contain custom files (such as robots.txt or +# .htaccess) here, relative to this directory. These files are copied +# directly to the root of the documentation. +#html_extra_path = [] + +# If not '', a 'Last updated on:' timestamp is inserted at every page bottom, +# using the given strftime format. +#html_last_updated_fmt = '%b %d, %Y' + +# If true, SmartyPants will be used to convert quotes and dashes to +# typographically correct entities. +#html_use_smartypants = True + +# Custom sidebar templates, maps document names to template names. +#html_sidebars = {} + +# Additional templates that should be rendered to pages, maps page names to +# template names. +#html_additional_pages = {} + +# If false, no module index is generated. +#html_domain_indices = True + +# If false, no index is generated. +#html_use_index = True + +# If true, the index is split into individual pages for each letter. +#html_split_index = False + +# If true, links to the reST sources are added to the pages. +#html_show_sourcelink = True + +# If true, "Created using Sphinx" is shown in the HTML footer. Default is True. +#html_show_sphinx = True + +# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. +#html_show_copyright = True + +# If true, an OpenSearch description file will be output, and all pages will +# contain a tag referring to it. The value of this option must be the +# base URL from which the finished HTML is served. +#html_use_opensearch = '' + +# This is the file name suffix for HTML files (e.g. ".xhtml"). +#html_file_suffix = None + +# Output file base name for HTML help builder. +htmlhelp_basename = 'AXUIdocumentdoc' + + +# -- Options for LaTeX output --------------------------------------------- + +latex_elements = { +# The paper size ('letterpaper' or 'a4paper'). +#'papersize': 'letterpaper', + +# The font size ('10pt', '11pt' or '12pt'). +#'pointsize': '10pt', + +# Additional stuff for the LaTeX preamble. +#'preamble': '', +} + +# Grouping the document tree into LaTeX files. List of tuples +# (source start file, target name, title, +# author, documentclass [howto, manual, or own class]). +latex_documents = [ + ('index', 'AXUIdocument.tex', u'AXUI document Documentation', + u'xcgspring', 'manual'), +] + +# The name of an image file (relative to this directory) to place at the top of +# the title page. +#latex_logo = None + +# For "manual" documents, if this is true, then toplevel headings are parts, +# not chapters. +#latex_use_parts = False + +# If true, show page references after internal links. +#latex_show_pagerefs = False + +# If true, show URL addresses after external links. +#latex_show_urls = False + +# Documents to append as an appendix to all manuals. +#latex_appendices = [] + +# If false, no module index is generated. +#latex_domain_indices = True + + +# -- Options for manual page output --------------------------------------- + +# One entry per manual page. List of tuples +# (source start file, name, description, authors, manual section). +man_pages = [ + ('index', 'axuidocument', u'AXUI document Documentation', + [u'xcgspring'], 1) +] + +# If true, show URL addresses after external links. +#man_show_urls = False + + +# -- Options for Texinfo output ------------------------------------------- + +# Grouping the document tree into Texinfo files. List of tuples +# (source start file, target name, title, author, +# dir menu entry, description, category) +texinfo_documents = [ + ('index', 'AXUIdocument', u'AXUI document Documentation', + u'xcgspring', 'AXUIdocument', 'One line description of project.', + 'Miscellaneous'), +] + +# Documents to append as an appendix to all manuals. +#texinfo_appendices = [] + +# If false, no module index is generated. +#texinfo_domain_indices = True + +# How to display URL addresses: 'footnote', 'no', or 'inline'. +#texinfo_show_urls = 'footnote' + +# If true, do not generate a @detailmenu in the "Top" node's menu. +#texinfo_no_detailmenu = False + + +# -- Options for Epub output ---------------------------------------------- + +# Bibliographic Dublin Core info. +epub_title = u'AXUI document' +epub_author = u'xcgspring' +epub_publisher = u'xcgspring' +epub_copyright = u'2015, xcgspring' + +# The basename for the epub file. It defaults to the project name. +#epub_basename = u'AXUI document' + +# The HTML theme for the epub output. Since the default themes are not optimized +# for small screen space, using the same theme for HTML and epub output is +# usually not wise. This defaults to 'epub', a theme designed to save visual +# space. +#epub_theme = 'epub' + +# The language of the text. It defaults to the language option +# or en if the language is not set. +#epub_language = '' + +# The scheme of the identifier. Typical schemes are ISBN or URL. +#epub_scheme = '' + +# The unique identifier of the text. This can be a ISBN number +# or the project homepage. +#epub_identifier = '' + +# A unique identification for the text. +#epub_uid = '' + +# A tuple containing the cover image and cover page html template filenames. +#epub_cover = () + +# A sequence of (type, uri, title) tuples for the guide element of content.opf. +#epub_guide = () + +# HTML files that should be inserted before the pages created by sphinx. +# The format is a list of tuples containing the path and title. +#epub_pre_files = [] + +# HTML files shat should be inserted after the pages created by sphinx. +# The format is a list of tuples containing the path and title. +#epub_post_files = [] + +# A list of files that should not be packed into the epub file. +epub_exclude_files = ['search.html'] + +# The depth of the table of contents in toc.ncx. +#epub_tocdepth = 3 + +# Allow duplicate toc entries. +#epub_tocdup = True + +# Choose between 'default' and 'includehidden'. +#epub_tocscope = 'default' + +# Fix unsupported image types using the PIL. +#epub_fix_images = False + +# Scale large images. +#epub_max_image_width = 0 + +# How to display URL addresses: 'footnote', 'no', or 'inline'. +#epub_show_urls = 'inline' + +# If false, no index is generated. +#epub_use_index = True diff --git a/example/windows/wmplayer_wrapper.py b/example/windows/wmplayer_wrapper.py index 467b554..678ca14 100644 --- a/example/windows/wmplayer_wrapper.py +++ b/example/windows/wmplayer_wrapper.py @@ -1,93 +1,93 @@ -'''for wmplayer operations, win8.1 os - - 'wmplayer_start', - 'wmplayer_close', - - 'wmplayer_open_media_file', - 'wmplayer_play' - 'wmplayer_pause' - 'wmplayer_set_repeat_on', - 'wmplayer_set_repeat_off' - 'wmplayer_volume_up' - 'wmplayer_volume_down' - 'wmplayer_mute' - 'wmplayer_unmute' - 'wmplayer_seek' - - 'wmplayer_wait_playback_end', -''' - -import os -import AXUI - -config_file = "windows.cfg" -app_map = "windows_media_player.xml" - -AXUI.Config(config_file) -appmap = AXUI.AppMap(app_map) - -def wmplayer_start(): - appmap.wmplayer_Window.start() - -def wmplayer_close(): - appmap.wmplayer_Window.stop() - -def wmplayer_open_media_file(media_file): - if not os.path.isfile(media_file): - print("media file not exist: %s" % media_file) - return - appmap.wmplayer_Window.Open_Dialog.FileName_ComboBox.FileName_Edit.ValuePattern.SetValue(media_file) - appmap.wmplayer_Window.Open_Dialog.Open_Button.InvokePattern.Invoke() - -def wmplayer_play(): - appmap.wmplayer_Window.TransportSubview_Group.Play_Pause_Button.start() - if appmap.wmplayer_Window.TransportSubview_Group.Play_Pause_Button.Name == "Play": - appmap.wmplayer_Window.TransportSubview_Group.Play_Pause_Button.InvokePattern.Invoke() - -def wmplayer_pause(): - appmap.wmplayer_Window.TransportSubview_Group.Play_Pause_Button.start() - if appmap.wmplayer_Window.TransportSubview_Group.Play_Pause_Button.Name == "Pause": - appmap.wmplayer_Window.TransportSubview_Group.Play_Pause_Button.InvokePattern.Invoke() - -def wmplayer_set_repeat_on(): - appmap.wmplayer_Window.TransportSubview_Group.repeat_Button.start() - if appmap.wmplayer_Window.TransportSubview_Group.repeat_Button.Name == "Turn repeat on": - appmap.wmplayer_Window.TransportSubview_Group.repeat_Button.InvokePattern.Invoke() - -def wmplayer_set_repeat_off(): - appmap.wmplayer_Window.TransportSubview_Group.repeat_Button.start() - if appmap.wmplayer_Window.TransportSubview_Group.repeat_Button.Name == "Turn repeat off": - appmap.wmplayer_Window.TransportSubview_Group.repeat_Button.InvokePattern.Invoke() - -def wmplayer_volume_up(): - appmap.wmplayer_Window.keyboard.Input('{F9}') - -def wmplayer_volume_down(): - appmap.wmplayer_Window.keyboard.Input('{F8}') - -def wmplayer_mute(): - appmap.wmplayer_Window.MenuBar.Play_MenuItem.SetFocus() - if int(appmap.wmplayer_Window.Volume_Menu.Mute_MenuItem.LegacyIAccessiblePattern.CurrentState) == int(0): - appmap.wmplayer_Window.Volume_Menu.Mute_MenuItem.InvokePattern.Invoke() - -def wmplayer_unmute(): - appmap.wmplayer_Window.MenuBar.Play_MenuItem.SetFocus() - if int(appmap.wmplayer_Window.Volume_Menu.Mute_MenuItem.LegacyIAccessiblePattern.CurrentState) == int(16): - appmap.wmplayer_Window.Volume_Menu.Mute_MenuItem.InvokePattern.Invoke() - -def wmplayer_seek(seek_value): - left, right, top, bottom = appmap.wmplayer_Window.Seek_Slider.coordinate - - x = (right-left)*(seek_value/100)+left - y = (bottom+top)/2 - - appmap.wmplayer_Window.Seek_Slider.SetFocus() - appmap.wmplayer_Window.Seek_Slider.Mouse.left_click((x,y)) - -def wmplayer_wait_playback_end(): - import time - while True: - time.sleep(1) - if appmap.wmplayer_Window.TransportSubview_Group.Play_Pause_Button.Name == "Play": - break +'''for wmplayer operations, win8.1 os + + 'wmplayer_start', + 'wmplayer_close', + + 'wmplayer_open_media_file', + 'wmplayer_play' + 'wmplayer_pause' + 'wmplayer_set_repeat_on', + 'wmplayer_set_repeat_off' + 'wmplayer_volume_up' + 'wmplayer_volume_down' + 'wmplayer_mute' + 'wmplayer_unmute' + 'wmplayer_seek' + + 'wmplayer_wait_playback_end', +''' + +import os +import AXUI + +config_file = "windows.cfg" +app_map = "windows_media_player.xml" + +AXUI.Config(config_file) +appmap = AXUI.AppMap(app_map) + +def wmplayer_start(): + appmap.wmplayer_Window.start() + +def wmplayer_close(): + appmap.wmplayer_Window.stop() + +def wmplayer_open_media_file(media_file): + if not os.path.isfile(media_file): + print(("media file not exist: %s" % media_file)) + return + appmap.wmplayer_Window.Open_Dialog.FileName_ComboBox.FileName_Edit.ValuePattern.SetValue(media_file) + appmap.wmplayer_Window.Open_Dialog.Open_Button.InvokePattern.Invoke() + +def wmplayer_play(): + appmap.wmplayer_Window.TransportSubview_Group.Play_Pause_Button.start() + if appmap.wmplayer_Window.TransportSubview_Group.Play_Pause_Button.Name == "Play": + appmap.wmplayer_Window.TransportSubview_Group.Play_Pause_Button.InvokePattern.Invoke() + +def wmplayer_pause(): + appmap.wmplayer_Window.TransportSubview_Group.Play_Pause_Button.start() + if appmap.wmplayer_Window.TransportSubview_Group.Play_Pause_Button.Name == "Pause": + appmap.wmplayer_Window.TransportSubview_Group.Play_Pause_Button.InvokePattern.Invoke() + +def wmplayer_set_repeat_on(): + appmap.wmplayer_Window.TransportSubview_Group.repeat_Button.start() + if appmap.wmplayer_Window.TransportSubview_Group.repeat_Button.Name == "Turn repeat on": + appmap.wmplayer_Window.TransportSubview_Group.repeat_Button.InvokePattern.Invoke() + +def wmplayer_set_repeat_off(): + appmap.wmplayer_Window.TransportSubview_Group.repeat_Button.start() + if appmap.wmplayer_Window.TransportSubview_Group.repeat_Button.Name == "Turn repeat off": + appmap.wmplayer_Window.TransportSubview_Group.repeat_Button.InvokePattern.Invoke() + +def wmplayer_volume_up(): + appmap.wmplayer_Window.keyboard.Input('{F9}') + +def wmplayer_volume_down(): + appmap.wmplayer_Window.keyboard.Input('{F8}') + +def wmplayer_mute(): + appmap.wmplayer_Window.MenuBar.Play_MenuItem.SetFocus() + if int(appmap.wmplayer_Window.Volume_Menu.Mute_MenuItem.LegacyIAccessiblePattern.CurrentState) == int(0): + appmap.wmplayer_Window.Volume_Menu.Mute_MenuItem.InvokePattern.Invoke() + +def wmplayer_unmute(): + appmap.wmplayer_Window.MenuBar.Play_MenuItem.SetFocus() + if int(appmap.wmplayer_Window.Volume_Menu.Mute_MenuItem.LegacyIAccessiblePattern.CurrentState) == int(16): + appmap.wmplayer_Window.Volume_Menu.Mute_MenuItem.InvokePattern.Invoke() + +def wmplayer_seek(seek_value): + left, right, top, bottom = appmap.wmplayer_Window.Seek_Slider.coordinate + + x = (right-left)*(seek_value/100)+left + y = (bottom+top)/2 + + appmap.wmplayer_Window.Seek_Slider.SetFocus() + appmap.wmplayer_Window.Seek_Slider.Mouse.left_click((x,y)) + +def wmplayer_wait_playback_end(): + import time + while True: + time.sleep(1) + if appmap.wmplayer_Window.TransportSubview_Group.Play_Pause_Button.Name == "Play": + break \ No newline at end of file diff --git a/example/windows/wmplayer_wrapper.py.bak b/example/windows/wmplayer_wrapper.py.bak new file mode 100644 index 0000000..467b554 --- /dev/null +++ b/example/windows/wmplayer_wrapper.py.bak @@ -0,0 +1,93 @@ +'''for wmplayer operations, win8.1 os + + 'wmplayer_start', + 'wmplayer_close', + + 'wmplayer_open_media_file', + 'wmplayer_play' + 'wmplayer_pause' + 'wmplayer_set_repeat_on', + 'wmplayer_set_repeat_off' + 'wmplayer_volume_up' + 'wmplayer_volume_down' + 'wmplayer_mute' + 'wmplayer_unmute' + 'wmplayer_seek' + + 'wmplayer_wait_playback_end', +''' + +import os +import AXUI + +config_file = "windows.cfg" +app_map = "windows_media_player.xml" + +AXUI.Config(config_file) +appmap = AXUI.AppMap(app_map) + +def wmplayer_start(): + appmap.wmplayer_Window.start() + +def wmplayer_close(): + appmap.wmplayer_Window.stop() + +def wmplayer_open_media_file(media_file): + if not os.path.isfile(media_file): + print("media file not exist: %s" % media_file) + return + appmap.wmplayer_Window.Open_Dialog.FileName_ComboBox.FileName_Edit.ValuePattern.SetValue(media_file) + appmap.wmplayer_Window.Open_Dialog.Open_Button.InvokePattern.Invoke() + +def wmplayer_play(): + appmap.wmplayer_Window.TransportSubview_Group.Play_Pause_Button.start() + if appmap.wmplayer_Window.TransportSubview_Group.Play_Pause_Button.Name == "Play": + appmap.wmplayer_Window.TransportSubview_Group.Play_Pause_Button.InvokePattern.Invoke() + +def wmplayer_pause(): + appmap.wmplayer_Window.TransportSubview_Group.Play_Pause_Button.start() + if appmap.wmplayer_Window.TransportSubview_Group.Play_Pause_Button.Name == "Pause": + appmap.wmplayer_Window.TransportSubview_Group.Play_Pause_Button.InvokePattern.Invoke() + +def wmplayer_set_repeat_on(): + appmap.wmplayer_Window.TransportSubview_Group.repeat_Button.start() + if appmap.wmplayer_Window.TransportSubview_Group.repeat_Button.Name == "Turn repeat on": + appmap.wmplayer_Window.TransportSubview_Group.repeat_Button.InvokePattern.Invoke() + +def wmplayer_set_repeat_off(): + appmap.wmplayer_Window.TransportSubview_Group.repeat_Button.start() + if appmap.wmplayer_Window.TransportSubview_Group.repeat_Button.Name == "Turn repeat off": + appmap.wmplayer_Window.TransportSubview_Group.repeat_Button.InvokePattern.Invoke() + +def wmplayer_volume_up(): + appmap.wmplayer_Window.keyboard.Input('{F9}') + +def wmplayer_volume_down(): + appmap.wmplayer_Window.keyboard.Input('{F8}') + +def wmplayer_mute(): + appmap.wmplayer_Window.MenuBar.Play_MenuItem.SetFocus() + if int(appmap.wmplayer_Window.Volume_Menu.Mute_MenuItem.LegacyIAccessiblePattern.CurrentState) == int(0): + appmap.wmplayer_Window.Volume_Menu.Mute_MenuItem.InvokePattern.Invoke() + +def wmplayer_unmute(): + appmap.wmplayer_Window.MenuBar.Play_MenuItem.SetFocus() + if int(appmap.wmplayer_Window.Volume_Menu.Mute_MenuItem.LegacyIAccessiblePattern.CurrentState) == int(16): + appmap.wmplayer_Window.Volume_Menu.Mute_MenuItem.InvokePattern.Invoke() + +def wmplayer_seek(seek_value): + left, right, top, bottom = appmap.wmplayer_Window.Seek_Slider.coordinate + + x = (right-left)*(seek_value/100)+left + y = (bottom+top)/2 + + appmap.wmplayer_Window.Seek_Slider.SetFocus() + appmap.wmplayer_Window.Seek_Slider.Mouse.left_click((x,y)) + +def wmplayer_wait_playback_end(): + import time + while True: + time.sleep(1) + if appmap.wmplayer_Window.TransportSubview_Group.Play_Pause_Button.Name == "Play": + break + \ No newline at end of file diff --git a/test/test_config/fake_module.py b/test/test_config/fake_module.py index 02121cc..524c777 100644 --- a/test/test_config/fake_module.py +++ b/test/test_config/fake_module.py @@ -7,6 +7,6 @@ def config(configs=default_configs): - print configs["config1"] - print configs["config2"] - print configs["config3"] + print(configs["config1"]) + print(configs["config2"]) + print(configs["config3"]) diff --git a/test/test_config/fake_module.py.bak b/test/test_config/fake_module.py.bak new file mode 100644 index 0000000..02121cc --- /dev/null +++ b/test/test_config/fake_module.py.bak @@ -0,0 +1,12 @@ + +config_section="fake_module" +default_configs={ "config1":"config_set1", + "config2":"config_set2", + "config3":"config_set3", + } + + +def config(configs=default_configs): + print configs["config1"] + print configs["config2"] + print configs["config3"] diff --git a/test/test_driver/windows/test_Translater.py b/test/test_driver/windows/test_Translater.py index c96cf90..378260b 100644 --- a/test/test_driver/windows/test_Translater.py +++ b/test/test_driver/windows/test_Translater.py @@ -10,7 +10,7 @@ def test_coordinate_identifier(self): identifier = "Coordinate = '(12 ,34, 56, 79)'" parsed_identifier = identifier_parser.parse(identifier, lexer=identifier_lexer) translated_identifier = translater.ID_Translater(parsed_identifier).get_translated() - print translated_identifier + print(translated_identifier) @unittest.skipUnless(sys.platform.startswith("win"), "requires Windows") def test_index_identifier(self): @@ -19,7 +19,7 @@ def test_index_identifier(self): identifier = "Name='menu bar' AND Index=3" parsed_identifier = identifier_parser.parse(identifier, lexer=identifier_lexer) translated_identifier = translater.ID_Translater(parsed_identifier).get_translated() - print translated_identifier + print(translated_identifier) @unittest.skipUnless(sys.platform.startswith("win"), "requires Windows") def test_UIA_identifier(self): @@ -28,5 +28,5 @@ def test_UIA_identifier(self): identifier = "Name='menu bar' AND LocalizedControlType='menu bar'" parsed_identifier = identifier_parser.parse(identifier, lexer=identifier_lexer) translated_identifier = translater.ID_Translater(parsed_identifier).get_translated() - print translated_identifier + print(translated_identifier) diff --git a/test/test_driver/windows/test_Translater.py.bak b/test/test_driver/windows/test_Translater.py.bak new file mode 100644 index 0000000..c96cf90 --- /dev/null +++ b/test/test_driver/windows/test_Translater.py.bak @@ -0,0 +1,32 @@ + +import sys +import unittest + +class TestTranslater(unittest.TestCase): + @unittest.skipUnless(sys.platform.startswith("win"), "requires Windows") + def test_coordinate_identifier(self): + import AXUI.driver.windows.Translater as translater + from AXUI.parsing.identifier_parsing import identifier_lexer, identifier_parser + identifier = "Coordinate = '(12 ,34, 56, 79)'" + parsed_identifier = identifier_parser.parse(identifier, lexer=identifier_lexer) + translated_identifier = translater.ID_Translater(parsed_identifier).get_translated() + print translated_identifier + + @unittest.skipUnless(sys.platform.startswith("win"), "requires Windows") + def test_index_identifier(self): + import AXUI.driver.windows.Translater as translater + from AXUI.parsing.identifier_parsing import identifier_lexer, identifier_parser + identifier = "Name='menu bar' AND Index=3" + parsed_identifier = identifier_parser.parse(identifier, lexer=identifier_lexer) + translated_identifier = translater.ID_Translater(parsed_identifier).get_translated() + print translated_identifier + + @unittest.skipUnless(sys.platform.startswith("win"), "requires Windows") + def test_UIA_identifier(self): + import AXUI.driver.windows.Translater as translater + from AXUI.parsing.identifier_parsing import identifier_lexer, identifier_parser + identifier = "Name='menu bar' AND LocalizedControlType='menu bar'" + parsed_identifier = identifier_parser.parse(identifier, lexer=identifier_lexer) + translated_identifier = translater.ID_Translater(parsed_identifier).get_translated() + print translated_identifier + diff --git a/test/test_driver/windows/test_win32.py b/test/test_driver/windows/test_win32.py index b2fd61c..44fe844 100644 --- a/test/test_driver/windows/test_win32.py +++ b/test/test_driver/windows/test_win32.py @@ -21,7 +21,7 @@ def test_keyboard(self): def test_mouse(self): from AXUI.driver.windows.win32 import Mouse mouse = Mouse(FakeUIElement()) - print mouse + print(mouse) mouse.RightClick() diff --git a/test/test_driver/windows/test_win32.py.bak b/test/test_driver/windows/test_win32.py.bak new file mode 100644 index 0000000..b2fd61c --- /dev/null +++ b/test/test_driver/windows/test_win32.py.bak @@ -0,0 +1,27 @@ + +import sys +import unittest + +class FakeUIElement(object): + def SetFocus(self): + pass + + def GetClickablePoint(self): + return 600, 300 + + +class TestWin32(unittest.TestCase): + @unittest.skipUnless(sys.platform.startswith("win"), "requires Windows") + def test_keyboard(self): + from AXUI.driver.windows.win32 import Keyboard + keyboard = Keyboard(FakeUIElement()) + keyboard.Input("~d") + + @unittest.skipUnless(sys.platform.startswith("win"), "requires Windows") + def test_mouse(self): + from AXUI.driver.windows.win32 import Mouse + mouse = Mouse(FakeUIElement()) + print mouse + mouse.RightClick() + + diff --git a/test/test_parsing/test_cli_command_parsing.py b/test/test_parsing/test_cli_command_parsing.py index 220a77a..7ce9e12 100644 --- a/test/test_parsing/test_cli_command_parsing.py +++ b/test/test_parsing/test_cli_command_parsing.py @@ -13,11 +13,11 @@ def test_lex(self): tok = cli_command_lexer.token() if not tok: break - print tok + print(tok) def test_yacc(self): from AXUI.parsing.cli_command_parsing import cli_command_lexer, cli_command_parser - print cli_command_parser.parse(self.command, lexer=cli_command_lexer) + print(cli_command_parser.parse(self.command, lexer=cli_command_lexer)) if __name__=="__main__": unittest.main(verbosity=2) diff --git a/test/test_parsing/test_cli_command_parsing.py.bak b/test/test_parsing/test_cli_command_parsing.py.bak new file mode 100644 index 0000000..220a77a --- /dev/null +++ b/test/test_parsing/test_cli_command_parsing.py.bak @@ -0,0 +1,24 @@ + +import unittest + +class TestAppMap(unittest.TestCase): + def setUp(self): + self.command = r'''"c:\test\test 1\1.exe" {variable} parameter2 parameter3''' + + def test_lex(self): + from AXUI.parsing.cli_command_parsing import cli_command_lexer + + cli_command_lexer.input(self.command) + while True: + tok = cli_command_lexer.token() + if not tok: + break + print tok + + def test_yacc(self): + from AXUI.parsing.cli_command_parsing import cli_command_lexer, cli_command_parser + print cli_command_parser.parse(self.command, lexer=cli_command_lexer) + +if __name__=="__main__": + unittest.main(verbosity=2) + diff --git a/test/test_parsing/test_gui_command_parsing.py b/test/test_parsing/test_gui_command_parsing.py index f6a976a..e1d8e42 100644 --- a/test/test_parsing/test_gui_command_parsing.py +++ b/test/test_parsing/test_gui_command_parsing.py @@ -13,11 +13,11 @@ def test_lex(self): tok = gui_command_lexer.token() if not tok: break - print tok + print(tok) def test_yacc(self): from AXUI.parsing.gui_command_parsing import gui_command_lexer, gui_command_parser - print gui_command_parser.parse(self.command, lexer=gui_command_lexer) + print(gui_command_parser.parse(self.command, lexer=gui_command_lexer)) if __name__=="__main__": unittest.main(verbosity=2) diff --git a/test/test_parsing/test_gui_command_parsing.py.bak b/test/test_parsing/test_gui_command_parsing.py.bak new file mode 100644 index 0000000..f6a976a --- /dev/null +++ b/test/test_parsing/test_gui_command_parsing.py.bak @@ -0,0 +1,24 @@ + +import unittest + +class TestAppMap(unittest.TestCase): + def setUp(self): + self.command = '''x.y.asfas 'asf' 123 'true' true ('asdf', ('asdf', 12))''' + + def test_lex(self): + from AXUI.parsing.gui_command_parsing import gui_command_lexer + + gui_command_lexer.input(self.command) + while True: + tok = gui_command_lexer.token() + if not tok: + break + print tok + + def test_yacc(self): + from AXUI.parsing.gui_command_parsing import gui_command_lexer, gui_command_parser + print gui_command_parser.parse(self.command, lexer=gui_command_lexer) + +if __name__=="__main__": + unittest.main(verbosity=2) + diff --git a/test/test_parsing/test_identifier_parsing.py b/test/test_parsing/test_identifier_parsing.py index a6444d9..f01be93 100644 --- a/test/test_parsing/test_identifier_parsing.py +++ b/test/test_parsing/test_identifier_parsing.py @@ -13,9 +13,9 @@ def test_lex(self): tok = identifier_lexer.token() if not tok: break - print tok + print(tok) def test_yacc(self): from AXUI.parsing.identifier_parsing import identifier_lexer, identifier_parser - print identifier_parser.parse(self.identifier, lexer=identifier_lexer) + print(identifier_parser.parse(self.identifier, lexer=identifier_lexer)) diff --git a/test/test_parsing/test_identifier_parsing.py.bak b/test/test_parsing/test_identifier_parsing.py.bak new file mode 100644 index 0000000..a6444d9 --- /dev/null +++ b/test/test_parsing/test_identifier_parsing.py.bak @@ -0,0 +1,21 @@ + +import unittest + +class TestAppMap(unittest.TestCase): + def setUp(self): + self.identifier = 'Name=\'xxx\' and class = "yyy" or ((id = 123 and index = 1) and enabled = tRUE)' + + def test_lex(self): + from AXUI.parsing.identifier_parsing import identifier_lexer + + identifier_lexer.input(self.identifier) + while True: + tok = identifier_lexer.token() + if not tok: + break + print tok + + def test_yacc(self): + from AXUI.parsing.identifier_parsing import identifier_lexer, identifier_parser + print identifier_parser.parse(self.identifier, lexer=identifier_lexer) + diff --git a/tools/check_app_map.py b/tools/check_app_map.py index 7b9c674..893a9e1 100644 --- a/tools/check_app_map.py +++ b/tools/check_app_map.py @@ -14,12 +14,12 @@ def check_app_map(xs_module, app_map): app_map_instance = xs_module.CreateFromDocument(app_map) print("Check successful") except pyxb.UnrecognizedContentError as e: - print(e.details()) + print((e.details())) except pyxb.IncompleteElementContentError as e: - print(e.details()) + print((e.details())) if __name__=="__main__": - print("Usage:\npython %s XSD_file(abs path) app_map_file" % __file__) + print(("Usage:\npython %s XSD_file(abs path) app_map_file" % __file__)) XSD_file = sys.argv[1] app_map_file = sys.argv[2] XSD_module_name = os.path.basename(XSD_file).split(".")[0] diff --git a/tools/check_app_map.py.bak b/tools/check_app_map.py.bak new file mode 100644 index 0000000..7b9c674 --- /dev/null +++ b/tools/check_app_map.py.bak @@ -0,0 +1,36 @@ + +import os +import sys +try: + import pyxb +except ImportError: + print("Need install pyxb first") + sys.exit(1) + +from generate_all_schema_modules import generate_module + +def check_app_map(xs_module, app_map): + try: + app_map_instance = xs_module.CreateFromDocument(app_map) + print("Check successful") + except pyxb.UnrecognizedContentError as e: + print(e.details()) + except pyxb.IncompleteElementContentError as e: + print(e.details()) + +if __name__=="__main__": + print("Usage:\npython %s XSD_file(abs path) app_map_file" % __file__) + XSD_file = sys.argv[1] + app_map_file = sys.argv[2] + XSD_module_name = os.path.basename(XSD_file).split(".")[0] + XSD_module_dir = os.path.dirname(XSD_file) + generate_module(XSD_file, XSD_module_name, XSD_module_dir) + + sys.path.insert(0, XSD_module_dir) + XSD_module = __import__(XSD_module_name) + + with open(app_map_file) as app_map: + check_app_map(XSD_module, app_map.read()) + + + diff --git a/tools/count_lines.py b/tools/count_lines.py index 46a3a67..aa72682 100644 --- a/tools/count_lines.py +++ b/tools/count_lines.py @@ -7,4 +7,4 @@ file_lines = sum(1 for line in open(os.path.join(_root, _file))) total_lines += file_lines -print("total lines: %d" % total_lines) +print(("total lines: %d" % total_lines)) diff --git a/tools/count_lines.py.bak b/tools/count_lines.py.bak new file mode 100644 index 0000000..46a3a67 --- /dev/null +++ b/tools/count_lines.py.bak @@ -0,0 +1,10 @@ + +import os +test_dir = "../AXUI" +total_lines = 0 +for _root, _dirs, _files in os.walk(test_dir): + for _file in _files: + file_lines = sum(1 for line in open(os.path.join(_root, _file))) + total_lines += file_lines + +print("total lines: %d" % total_lines)