ctypes.ArgumentError при использовании kivy с pywinauto - PullRequest
5 голосов
/ 30 апреля 2019

У меня есть приложение kivy, которое может взаимодействовать с другими окнами с помощью модуля pywinauto. Приложение прекрасно работает в Linux (где pywinauto не используется), но в Windows появляется следующая ошибка, приложение даже не запускается:

C:\Program Files (x86)\Python36_64\lib\site-packages\pywinauto\__init__.py:80: UserWarning: Revert to STA COM threading mode
    warnings.warn("Revert to STA COM threading mode", UserWarning)
[INFO   ] [GL          ] NPOT texture support is available
[INFO   ] [Base        ] Start application main loop
Traceback (most recent call last):
File ".\application.py", line 368, in <module>
    Application().run()
File "C:\Program Files (x86)\Python36_64\lib\site-packages\kivy\app.py", line 826, in run
    runTouchApp()
File "C:\Program Files (x86)\Python36_64\lib\site-packages\kivy\base.py", line 477, in runTouchApp
    EventLoop.start()
File "C:\Program Files (x86)\Python36_64\lib\site-packages\kivy\base.py", line 164, in start
    provider.start()
File "C:\Program Files (x86)\Python36_64\lib\site-packages\kivy\input\providers\wm_touch.py", line 68, in start
    self.hwnd, GWL_WNDPROC, self.new_windProc)
ctypes.ArgumentError: argument 3: <class 'TypeError'>: wrong type

Причина, по которой я думаю, что это проблема с pywinauto, заключается в том, что у меня есть следующие строки, и они прекрасно работают в Linux:

if SYSTEM == "Windows":
    import win32gui
    import win32process
    import wmi

    from pywinauto import application
    import pywinauto

Также я закомментирую строки импорта pywinauto, и он начинается. Это может быть связано с этой проблемой. Я действительно не знаю, какой код включить, так как он работает в других операционных системах .... Я предполагаю, что pywinauto изменяет что-то, что мешает работе Kivy.

У меня такой вопрос: как я могу использовать функциональность kivy и pywinauto в одном приложении?

1 Ответ

5 голосов
/ 01 мая 2019

Мне удалось воспроизвести поведение, используя:

  • Python 3.7.3 x64
  • Kivy 1.10.1
  • Pywinauto 0.6.6

В качестве примечания, я не работал ни с одним из 2-х пакетов ранее, я pip install отредактировал их специально для этогозадача.

Поскольку я не знал, как воспроизвести поведение, я просто скопировал MCVE из [GitHub]: pywinauto / pywinauto - ctypes.ArgumentError @ click_input (что вы также поделились в вопросе), и немного изменил его (только для ошибки, без стиля, улучшений, ... и т. д.).

code.py :

import random
from kivy.app           import App
from kivy.lang          import Builder
from kivy.core.window   import Window
from kivy.uix.boxlayout import BoxLayout

import pywinauto  # @TODO - cfati: moved after Kivy import(s), as it works otherwise (https://github.com/pywinauto/pywinauto/issues/419#issuecomment-488258224)


class DemoLayout(BoxLayout): pass
Builder.load_string("""
#: import datetime  datetime.datetime
<DemoLayout>:
  padding: 75

  Button:
    on_press: print(f"PRESSED @ {datetime.now()}")
""")


class Demo(App):

  def build(self):
    self.root = DemoLayout()

  def on_start(self):
    title = f"__KIVY_APP__{random.getrandbits(128)}"
    Window.set_title(title)
    hwnd = pywinauto.findwindows.find_window(title=title)
    app = pywinauto.Application()
    app.connect(handle=hwnd)
    window = app.window(handle=hwnd).wrapper_object()
    window.click_input(button="left", pressed="", coords=(100, 100), double=False, absolute=False)


Demo().run()

Вывод :

[cfati@CFATI-5510-0:e:\Work\Dev\StackOverflow\q055928463]> "e:\Work\Dev\VEnvs\py_064_03.07.03_test0\Scripts\python.exe" code.py
[INFO   ] [Logger      ] Record log in C:\Users\cfati\.kivy\logs\kivy_19-05-01_83.txt
[INFO   ] [Kivy        ] v1.10.1
[INFO   ] [Python      ] v3.7.3 (v3.7.3:ef4ec6ed12, Mar 25 2019, 22:22:05) [MSC v.1916 64 bit (AMD64)]
[INFO   ] [Factory     ] 194 symbols loaded
[INFO   ] [Image       ] Providers: img_tex, img_dds, img_sdl2, img_pil, img_gif (img_ffpyplayer ignored)
[INFO   ] [Window      ] Provider: sdl2
[INFO   ] [GL          ] Using the "OpenGL" graphics system
[INFO   ] [GL          ] GLEW initialization succeeded
[INFO   ] [GL          ] Backend used <glew>
[INFO   ] [GL          ] OpenGL version <b'4.5.0 - Build 23.20.16.4973'>
[INFO   ] [GL          ] OpenGL vendor <b'Intel'>
[INFO   ] [GL          ] OpenGL renderer <b'Intel(R) HD Graphics 530'>
[INFO   ] [GL          ] OpenGL parsed version: 4, 5
[INFO   ] [GL          ] Shading version <b'4.50 - Build 23.20.16.4973'>
[INFO   ] [GL          ] Texture max size <16384>
[INFO   ] [GL          ] Texture max units <32>
[INFO   ] [Window      ] auto add sdl2 input provider
[INFO   ] [Window      ] virtual keyboard not allowed, single mode, not docked
 e:\Work\Dev\VEnvs\py_064_03.07.03_test0\lib\site-packages\pywinauto\__init__.py:80: UserWarning: Revert to STA COM threading mode
   warnings.warn("Revert to STA COM threading mode", UserWarning)
[INFO   ] [Text        ] Provider: sdl2
[INFO   ] [Base        ] Start application main loop
 Traceback (most recent call last):
   File "code.py", line 36, in <module>
     Demo().run()
   File "e:\Work\Dev\VEnvs\py_064_03.07.03_test0\lib\site-packages\kivy\app.py", line 826, in run
     runTouchApp()
   File "e:\Work\Dev\VEnvs\py_064_03.07.03_test0\lib\site-packages\kivy\base.py", line 477, in runTouchApp
     EventLoop.start()
   File "e:\Work\Dev\VEnvs\py_064_03.07.03_test0\lib\site-packages\kivy\base.py", line 164, in start
     provider.start()
   File "e:\Work\Dev\VEnvs\py_064_03.07.03_test0\lib\site-packages\kivy\input\providers\wm_touch.py", line 68, in start
     self.hwnd, GWL_WNDPROC, self.new_windProc)
 ctypes.ArgumentError: argument 3: <class 'TypeError'>: wrong type

Прежде чем идти дальше, я хочу отметить:

  1. [Python 3.Docs]: ctypes - библиотека сторонних функций для Python
  2. [MS.Docs]: функция SetWindowLongPtrW

Как видно из последнего, SetWindowLongPtrW 3 * 1052Аргумент * rd может быть DWORD , HANDLE , указателем на функцию, в зависимости от значения аргумента 2 nd : в основном это void*это может быть сопоставлено с чем угодно.
Оба модуля вызывают эту функцию через ctypes :

Объяснение :

  • user32.dll загружается только один раз в текущем Python процессе
  • Код, как указано выше, инициализирует функции и обычно выполняется только один раз, во время импорта модуля (он может быть выполнен столько раз, сколько необходимо, но это снизит производительность)
  • Оба модуля определяют прототип функции (windll.user32.SetWindowLongPtrW, как мы на 64bit ), но они делают это по-разному
  • Из вышеприведенных 3 получается, что модуль, который импортируется последним, решает, как будет выглядеть прототип функции
  • Когда модуль, которыйВашингтонs import 1 st пытается использовать прототип, не соответствует передаваемым аргументам , следовательно, ошибка

Вот почему я должен был переместиться Pywinauto импортировать после Kivy , так что Kivy пытался вызвать функцию, используя прототип Pywinauto , иначе это сработало бы.
Можно пойти другим путем, но я не удосужился найти сценарий, в котором Pywinauto будет вызывать функцию, поскольку она неактуальна.

Глядя на 2 ctypes прототипы и C один (из MS URL ), получается, что:

  • Pywinauto делает все правильно (мне интересно, как это будет работать на 32bit , хотя)
  • Kivy использует только SetWindowLongPtrW сценарий использования (тот, что с указателем на функцию), и чтобы упростить задачу, они адаптировали прототип для своего сценария.Тем не менее, это не совсем соответствует C прототипу , и это из моего PoV выглядит как неудачное решение ( gainarie )

Я изменил мою Kivy установку и tadaa !(это Пасхальный заяц! :)):

Working

Примечание : здесь встречается (более простой) вариант: [SO]: Как предотвратить конфликты pynput и ctypes?

@ EDIT0 :

Я отправил [GitHub]: kivy / kivy - SetWindowLongPtrW ошибка прототипа ctypes , которая была объединена .Не уверен, когда он будет доступен на рынке ( PyPI , поэтому вы можете просто pip install его), однако.

В качестве альтернативы вы можете скачать патч и применить изменения локально.Проверьте [SO]: запускать / отлаживать UnitTests приложения Django из контекстного меню, вызываемого правой кнопкой мыши, в PyCharm Community Edition?(@ Ответ CristiFati) ( Patching utrunner section) о том, как применять патчи к Win (в основном, каждая строка, начинающаяся с one)"+" знак входит, и каждая строка, которая начинается с one "-" знак гаснет).Я использую Cygwin , кстати .
Или вы можете загрузить 3 измененных файла и перезаписать существующие.
В любом случае, сначала сохраните их !Кроме того, я не знаю, как изменения вписываются в более старые Kivy версии.

...