python中visible_only not found相关的问题其中的一个解决方法,关于pywinauto application中connect方法

PS:UP主只是某双非一本的测开实习生,在开发脚本的时候发现的这个问题,如果有说的不对的地方和写文章不好的地方请多指教。

涉及的额外概念:
进程和线程的区别

进程的优先级

pywinauto库

python自动化

进程中的线程在 CPU 上执行

写在题头:请检查你的python代码中有没有Application.connect这个方法(别的语言暂未知道是哪个方法名称),如果有的话,那么请继续看下去

1、出错原因

Application(object).connect(self, **kwargs)与window资源管理器explore.exe造成的一些冲突。

2、解决办法

在**kwargs中加入预设的时间参数,或者在这个函数前time.sleep(5)即可


3、基本原理(通过举例说明,以下是一个具体的例子)

(1)背景阐述,介绍这个例子

该例子是开发自动化脚本,要求模拟人的开关WiFi,蓝牙,飞行模式,但是当我打开右下角菜单的时候,出现了找不到WiFi、蓝牙控件的错误。

错误提示:visible_only not found我自己设定要找的字符串比如wlan,bluetooth

(2)windows资源管理器操作进程的原理

首先先说明一点,像桌面上的这种图标,包括你的文件夹

都是由window资源管理器这个进程加载的

有些叫windows资源管理器,有些叫explorer.exe,都一样

这个进程作为实现诸多功能的集大成者,是在开机的时候就已经将这个主进程基本启动了,

但是我们要知道windows为了优化开机速度,从而让某些进程在开机的时候只执行主进程,但是关联的子进程在开机之后才准备就绪,windows资源管理器我想也是如此。

开机之后,大家可以点击这个图标看一下,是不是这个进程打开的时候时间会慢,甚至有卡顿,这就是windows资源管理器在运行子进程的时候,这个优先级没那么高的子进程被人为的打开调用了。增加了该进程的优先级。

我们把这个子进程称作in进程(internet图标相关进程)

进程的状态:

那么就可以知道,刚刚开机的时候,in进程是新建状态

当我们打开右下角WiFi图标的时候,in进程就是执行状态

当我们进行了足够多关于windows资源管理器子进程的调用时(非in线程),那么资源管理器就会把in进程的优先级降低,从线程池中挤兑出去,那么in线程就会进入阻塞状态。

所以in进程基本上的3个状态我们都知道了,从打开时间来说

执行态>阻塞态>新建态

(3)我们为什么会出现错误的原理

打开python中相关的代码段,我通过debug是这里出问题

title的值是字符串,是我自己设定的想找到的按钮

再Ctrl+单击查看connect函数内部

查看到函数内部代码,这里可以发现端倪

(3.1)以下是Application.connect函数

    def connect(self, **kwargs):
        """Connect to an already running process

        The action is performed according to only one of parameters

        :param process: a process ID of the target
        :param handle: a window handle of the target
        :param path: a path used to launch the target
        :param timeout: a timeout for process start (relevant if path is specified)

        .. seealso::

           :func:`pywinauto.findwindows.find_elements` - the keyword arguments that
           are also can be used instead of **process**, **handle** or **path**
        """
        timeout = Timings.app_connect_timeout
        retry_interval = Timings.app_connect_retry
        if 'timeout' in kwargs and kwargs['timeout'] is not None:
            timeout = kwargs['timeout']
        if 'retry_interval' in kwargs and kwargs['retry_interval'] is not None:
            retry_interval = kwargs['retry_interval']

        connected = False
        if 'process' in kwargs:
            self.process = kwargs['process']
            try:
                wait_until(timeout, retry_interval, self.is_process_running, value=True)
            except TimeoutError:
                raise ProcessNotFoundError('Process with PID={} not found!'.format(self.process))
            connected = True

        elif 'handle' in kwargs:

            if not handleprops.iswindow(kwargs['handle']):
                message = "Invalid handle 0x%x passed to connect()" % (
                    kwargs['handle'])
                raise RuntimeError(message)

            self.process = handleprops.processid(kwargs['handle'])

            connected = True

        elif 'path' in kwargs:
            try:
                self.process = timings.wait_until_passes(
                        timeout, retry_interval, process_from_module,
                        ProcessNotFoundError, kwargs['path'],
                    )
            except TimeoutError:
                raise ProcessNotFoundError('Process "{}" not found!'.format(kwargs['path']))
            connected = True

        elif kwargs:
            kwargs['backend'] = self.backend.name
            if 'visible_only' not in kwargs:
                kwargs['visible_only'] = False
            if 'timeout' in kwargs:
                del kwargs['timeout']
                self.process = timings.wait_until_passes(
                        timeout, retry_interval, findwindows.find_element,
                        exceptions=(findwindows.ElementNotFoundError, findbestmatch.MatchError,
                                    controls.InvalidWindowHandle, controls.InvalidElement),
                        *(), **kwargs
                    ).process_id
            else:
                self.process = findwindows.find_element(**kwargs).process_id
            connected = True

        if not connected:
            raise RuntimeError(
                "You must specify some of process, handle, path or window search criteria.")

        if self.backend.name == 'win32':
            self.__warn_incorrect_bitness()

            if not handleprops.has_enough_privileges(self.process):
                warning_text = "Python process has no rights to make changes " \
                    "in the target GUI (run the script as Administrator)"
                warnings.warn(warning_text, UserWarning)

        return self

我传进来的参数没有时间,只有字符串,没有process,所以是调用默认的time,而且非path非handle,于是数据直接进入了elif kwargs这里,然后就出现了我们的错误,没有找到该控件,于是我们怀疑时间问题

于是Ctrl+单击查看timings函数

(3.2)以下是Timings

继续Ctrl+单击TimeConfig()

(3.3)以下是TimeConfig()函数

class TimeConfig(object):

    """Central storage and manipulation of timing values"""

    __default_timing = {
        'window_find_timeout': 5.,
        'window_find_retry': .09,

        'app_start_timeout': 10.,
        'app_start_retry': .90,

        'app_connect_timeout': 5.,
        'app_connect_retry': .1,

        'cpu_usage_interval': .5,
        'cpu_usage_wait_timeout': 20.,

        'exists_timeout': .5,
        'exists_retry': .3,

        'after_click_wait': .09,
        'after_clickinput_wait': .09,

        'after_menu_wait': .1,

        'after_sendkeys_key_wait': .01,

        'after_button_click_wait': 0,

        'before_closeclick_wait': .1,
        'closeclick_retry': .05,
        'closeclick_dialog_close_wait': 2.,
        'after_closeclick_wait': .2,

        'after_windowclose_timeout': 2,
        'after_windowclose_retry': .5,

        'after_setfocus_wait': .06,
        'setfocus_timeout': 2,
        'setfocus_retry': .1,

        'after_setcursorpos_wait': .01,

        'sendmessagetimeout_timeout': .01,

        'after_tabselect_wait': .05,

        'after_listviewselect_wait': .01,
        'after_listviewcheck_wait': .001,
        'listviewitemcontrol_timeout': 1.5,

        'after_treeviewselect_wait': .1,

        'after_toobarpressbutton_wait': .01,

        'after_updownchange_wait': .1,

        'after_movewindow_wait': 0,
        'after_buttoncheck_wait': 0,
        'after_comboboxselect_wait': 0.001,
        'after_listboxselect_wait': 0,
        'after_listboxfocuschange_wait': 0,
        'after_editsetedittext_wait': 0,
        'after_editselect_wait': 0.02,
        'drag_n_drop_move_mouse_wait': 0.1,
        'before_drag_wait': 0.2,
        'before_drop_wait': 0.1,
        'after_drag_n_drop_wait': 0.1,
        'scroll_step_wait': 0.1,

        'app_exit_timeout': 10.,
        'app_exit_retry': .1,
    }

    assert(__default_timing['window_find_timeout'] >=
           __default_timing['window_find_retry'] * 2)

    _timings = __default_timing.copy()
    _cur_speed = 1

    def __getattribute__(self, attr):
        """Get the value for a particular timing"""
        if attr in ['__dict__', '__members__', '__methods__', '__class__']:
            return object.__getattribute__(self, attr)

        if attr in dir(TimeConfig):
            return object.__getattribute__(self, attr)

        if attr in self.__default_timing:
            return self._timings[attr]
        else:
            raise AttributeError("Unknown timing setting: {0}".format(attr))

    def __setattr__(self, attr, value):
        """Set a particular timing"""
        if attr == '_timings':
            object.__setattr__(self, attr, value)
        elif attr in self.__default_timing:
            self._timings[attr] = value
        else:
            raise AttributeError("Unknown timing setting: {0}".format(attr))

    def fast(self):
        """Set fast timing values

        Currently this changes the timing in the following ways:
        timeouts = 1 second
        waits = 0 seconds
        retries = .001 seconds (minimum!)

        (if existing times are faster then keep existing times)
        """

        for setting in self.__default_timing:
            # set timeouts to the min of the current speed or 1 second
            if "_timeout" in setting:
                self._timings[setting] = \
                    min(1, self._timings[setting])

            if "_wait" in setting:
                self._timings[setting] = self._timings[setting] / 2

            elif setting.endswith("_retry"):
                self._timings[setting] = 0.001

            #self._timings['app_start_timeout'] = .5

    def slow(self):
        """Set slow timing values

        Currently this changes the timing in the following ways:
        timeouts = default timeouts * 10
        waits = default waits * 3
        retries = default retries * 3

        (if existing times are slower then keep existing times)
        """
        for setting in self.__default_timing:
            if "_timeout" in setting:
                self._timings[setting] = max(
                    self.__default_timing[setting] * 10,
                    self._timings[setting])

            if "_wait" in setting:
                self._timings[setting] = max(
                    self.__default_timing[setting] * 3,
                    self._timings[setting])

            elif setting.endswith("_retry"):
                self._timings[setting] = max(
                    self.__default_timing[setting] * 3,
                    self._timings[setting])

            if self._timings[setting] < .2:
                self._timings[setting] = .2

    def defaults(self):
        """Set all timings to the default time"""
        self._timings = self.__default_timing.copy()

    Fast = deprecated(fast)
    Slow = deprecated(slow)
    Defaults = deprecated(defaults)

这个函数很长,但是我们只需要关注与我们项目相关的时间(具体看你自己要用到哪个tieout),就知道原来是因为时间设定太短,导致出错。


(4)两个错误之间的联系,也就是2为什么会导致3

1:in进程并没有在一开机就进入线程池,而是被人为打开的时候才被创建,时间超长加时了

2:in线程在开机之后一直没有打开,于是进入了阻塞,再次被调用时间相对慢,但是比开机要快,所以这种情况多出现于一些笔记本电脑,一个线程的执行速度比较慢。所以会出现这个情况。​​​​​​​

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值