خودکار کردن رابط کاربری گرافیکی دسکتاپ در پایتون + pywinauto: چگونه با MS UI Automation دوست شویم

کتابخانه پایتون pywinauto — یک پروژه متن‌باز برای خودکارسازی برنامه‌های رابط کاربری گرافیکی دسکتاپ است Windowsدر طول دو سال گذشته، ویژگی‌های جدید و مهمی به آن اضافه شده است:

  • پشتیبانی از فناوری اتوماسیون رابط کاربری مایکروسافت. رابط کاربری همچنان یکسان است و اکنون از WinForms، WPF، Qt5 پشتیبانی می‌کند. Windows فروشگاه (UWP) و غیره - تقریباً هر چیزی که روشن است Windows.
  • سیستم Backend/Plugin (اکنون دو مورد از آنها در زیر هود وجود دارد: پیش فرض "win32" و جدید "uia"). سپس به آرامی به سمت کراس پلتفرم حرکت می کنیم.
  • قلاب های Win32 برای ماوس و صفحه کلید (کلیدهای داغ در روح pyHook).

ما همچنین یک مرور کوچک از آنچه در منبع باز برای اتوماسیون دسکتاپ در دسترس است (بدون ادعا برای مقایسه جدی) خواهیم داشت.

این مقاله متن بخشی از گزارشی از کنفرانس SQA Days 20 در مینسک است (نوار ویدئویی и اسلایدها)، نسخه تا حدی روسی راهنمای شروع به کار برای pywinauto.

بیایید با مروری کوتاه بر منبع باز در این زمینه شروع کنیم. برای برنامه‌های رابط کاربری گرافیکی دسکتاپ، همه چیز تا حدودی پیچیده‌تر از وب است که سلنیوم دارد. در اینجا رویکردهای اصلی وجود دارد:

روش مختصات

نقاط کلیک هاردکد، به امید موفقیت های خوب.
[+] کراس پلت فرم، آسان برای پیاده سازی.
[+] ایجاد یک رکورد "ضبط-بازپخش" از تست ها آسان است.
[-] ناپایدارترین نسبت به تغییر وضوح صفحه نمایش، تم، فونت، اندازه پنجره و غیره.
[-] تلاش زیادی برای پشتیبانی لازم است، اغلب آسان‌تر است که آزمایش‌ها را از ابتدا بازسازی کنید یا به صورت دستی آزمایش کنید.
[-] فقط اقدامات را خودکار می کند، روش های دیگری برای تأیید و استخراج داده ها وجود دارد.

ابزارها (کراس پلتفرم): اتوپی, pyautogui, PyUserInput و خیلی های دیگر. به عنوان یک قاعده، ابزارهای پیچیده تر شامل این قابلیت هستند (همیشه بین پلتفرم نیستند).

شایان ذکر است که روش مختصات می تواند رویکردهای دیگر را تکمیل کند. به عنوان مثال، برای گرافیک های سفارشی، می توانید روی مختصات نسبی کلیک کنید (از گوشه سمت چپ بالای پنجره / عنصر، و نه کل صفحه) - این معمولاً کاملاً قابل اعتماد است، به خصوص اگر طول / عرض را در نظر بگیرید. کل عنصر (سپس رزولوشن های مختلف صفحه نمایش آسیبی نمی بیند).

گزینه دیگر این است که فقط یک دستگاه با تنظیمات پایدار برای تست ها اختصاص دهید (نه کراس پلتفرم، اما در برخی موارد خوب است).

تشخیص تصاویر مرجع

[+] کراس پلت فرم
[+-] نسبتاً قابل اعتماد (بهتر از روش مختصات)، اما همچنان به ترفندهایی نیاز دارد.
[-+] نسبتا کند است، زیرا به منابع CPU برای الگوریتم های تشخیص نیاز دارد.
[-] تشخیص متن (OCR)، به عنوان یک قاعده، دور از ذهن است => شما نمی توانید داده های متنی را دریافت کنید. تا آنجا که من می دانم، راه حل های OCR موجود برای این نوع کارها چندان قابل اعتماد نیستند و به طور گسترده مورد استفاده قرار نمی گیرند (اگر قبلاً چنین نیست، در نظرات خوش آمدید).

ابزارها: سیکولی, لاکی (سازگار با Sikuli، پایتون خالص)، pyautogui.

فناوری دسترسی

[+] مطمئن ترین روش، زیرا به شما امکان می دهد بدون در نظر گرفتن نحوه ارائه متن توسط سیستم یا چارچوب، به جستجوی متن بپردازید.
[+] به شما امکان می دهد داده های متنی را استخراج کنید => تأیید نتایج آزمایش آسان تر است.
[+] به عنوان یک قاعده، سریعترین، زیرا تقریباً هیچ منبع CPU را مصرف نمی کند.
[-] ساخت یک ابزار چند پلتفرمی دشوار است: قطعاً همه کتابخانه‌های متن‌باز از یک یا دو فناوری دسترسی پشتیبانی می‌کنند. Windows/Linux/MacOS به طور کامل توسط هیچ کس پشتیبانی نمی‌شود، به جز آنهایی که پولی هستند مانند TestComplete، UFT یا Squish.
[-] چنین فناوری در اصل همیشه در دسترس نیست. به عنوان مثال، آزمایش صفحه بوت در داخل VirtualBox بدون تشخیص تصویر ضروری است. اما در بسیاری از موارد کلاسیک، رویکرد دسترسی همچنان قابل اجرا است. در مورد آن بیشتر و مورد بحث قرار خواهد گرفت.

ابزارها: TestStack.White در سی شارپ Winium.Desktop در سی شارپ (سازگار با سلنیوم)، درایور MS WinApp در سی شارپ (سازگار با اپیوم)، pywinauto, پیاتوم (سازگار با LDTP)، اتوماسیون رابط کاربری پایتون برایWindows, اتوماسیون در روبی LDTP (Linux پروژه تست دسکتاپ) و موارد مرتبط با آن Windows نسخه مار کبری.

LDTP شاید تنها ابزار متن باز بین پلتفرمی (به طور دقیق تر، خانواده ای از کتابخانه ها) بر اساس فناوری های دسترسی باشد. با این حال، او چندان محبوب نیست. من خودم از آن استفاده نکرده ام، اما طبق بررسی ها، رابط کاربری راحت ترین نیست. اگر نظرات مثبتی وجود دارد، لطفاً در نظرات به اشتراک بگذارید.

تست درب پشتی (معروف به داخل دوچرخه)

برای برنامه‌های چند پلتفرمی، خود توسعه‌دهندگان اغلب یک مکانیسم داخلی برای اطمینان از آزمایش‌پذیری ایجاد می‌کنند. به عنوان مثال، آنها یک سرویس دهنده TCP در برنامه ایجاد می کنند، آزمایش ها به آن متصل می شوند و دستورات متنی را ارسال می کنند: روی چه چیزی کلیک کنید، از کجا داده ها را دریافت کنید و غیره. قابل اعتماد، اما جهانی نیست.

فناوری های اصلی دسترسی دسکتاپ

API قدیمی Win32

بیشترین Windows برنامه‌هایی که قبل و بعد از انتشار WPF نوشته شده‌اند Windows Store، به نحوی بر اساس Win32 API ساخته شده‌اند. یعنی MFC، WTL، C++ Builder، Delphi، VB6 - همه این ابزارها از Win32 API استفاده می‌کنند. حتی Windows فرم‌ها تا حد زیادی با Win32 API سازگار هستند.

ابزارها: خودکار (شبیه به VB) و لفاف پایتون pyautoit, کلید خودکار (زبان خود، یک رابط IDispatch COM وجود دارد)، pywinauto (پایتون) اتوماسیون (یاقوت) win32-autogui (یاقوت).

اتوماسیون رابط کاربری مایکروسافت

مزیت اصلی: فناوری اتوماسیون رابط کاربری مایکروسافت از اکثر قریب به اتفاق برنامه‌های رابط کاربری گرافیکی پشتیبانی می‌کند. Windows با استثنائات نادر. مشکل: یادگیری آن خیلی آسان‌تر از API Win32 نیست. در غیر این صورت، هیچ‌کس برای آن wrapper نمی‌ساخت.

در واقع، این مجموعه ای از رابط های COM سفارشی است (عمدتا UIAutomationCore.dll) و همچنین دارای لفاف دات نت در فرم است namespace System.Windows.Automation. به هر حال، دارای یک اشکال معرفی شده است که به دلیل آن می توان برخی از عناصر UI را نادیده گرفت. بنابراین، بهتر است به طور مستقیم از UIAutomationCore.dll استفاده کنید (اگر در مورد UiaComWrapper در C # شنیده اید، پس همین است).

انواع رابط های COM:

(1) پایه IU شناخته شده "ریشه همه بدی ها" است. سطح پایین ترین، هرگز کاربر پسند.
(2) IDispatch و مشتقات (به عنوان مثال، Excel.Application) که می تواند در پایتون با استفاده از بسته win32com.client (شامل pyWin32) استفاده شود. راحت ترین و زیباترین گزینه.
(3) واسط های سفارشی که یک بسته Python شخص ثالث می تواند با آنها کار کند comtypes.

ابزارها: TestStack.White در سی شارپ pywinauto 0.6.0+ ، Winium.Desktop در سی شارپ اتوماسیون رابط کاربری پایتون برایWindows (کد منبع آنها برای بسته بندی های sish روی UIAutomationCore.dll فاش نشده است)، اتوماسیون روی روبی

AT-SPI

با وجود این واقعیت که تقریباً همه محورهای خانواده Linux این سیستم که بر اساس سیستم پنجره X ساخته شده است (در فدورا ۲۵، "X" با Wayland جایگزین شد)، "X" فقط به شما امکان می‌دهد پنجره‌های سطح بالا و ماوس/صفحه‌کلید را اجرا کنید. برای تجزیه و تحلیل دقیق دکمه‌ها، جعبه‌های لیست و غیره، فناوری AT-SPI وجود دارد. محبوب‌ترین مدیران پنجره دارای یک سرویس رجیستری به نام AT-SPI هستند که یک رابط کاربری گرافیکی خودکار برای برنامه‌ها فراهم می‌کند (حداقل Qt و GTK پشتیبانی می‌شوند).

ابزارها: پیاتسپی2.

به نظر من pyatspi2 دارای وابستگی های بسیار زیادی مانند همان PyGObject است. خود این فناوری به عنوان یک کتابخانه پویا معمولی در دسترس است libatspi.so. او دارد راهنمای مرجعبرای کتابخانه pywinauto، ما قصد داریم پشتیبانی AT-SPI را به این روش پیاده‌سازی کنیم: با بارگذاری libatspi.so و ماژول ctypes. تنها مشکل جزئی، استفاده از نسخه صحیح است، زیرا آنها برای برنامه‌های GTK+ و Qt کمی متفاوت هستند. نسخه احتمالی pywinauto 0.7.0 شامل پشتیبانی کامل خواهد بود. Linux می‌توان انتظار داشت که در نیمه اول سال ۲۰۱۸ باشد.

Apple Accessibility API

MacOS زبان اتوماسیون خود را دارد، AppleScript. برای پیاده سازی چنین چیزی در پایتون، البته باید از توابع ObjectiveC استفاده کنید. به نظر می رسد با شروع، حتی با MacOS 10.6، بسته pyobjc در پایتون از پیش نصب شده گنجانده شده است. همچنین فهرست کردن وابستگی‌ها برای پشتیبانی آینده در pywinauto را آسان‌تر می‌کند.

ابزارها: علاوه بر زبان اپل اسکریپت باید به آن توجه کنید ATOMac، با نام مستعار پیاتوم. این رابط با LDTP سازگار است، اما همچنین یک کتابخانه مستقل است. این دارد مثال اتوماسیون iTunes در macOSنوشته شده توسط شاگرد من یک مسئله شناخته شده وجود دارد: زمان بندی انعطاف پذیر کار نمی کند (روش ها waitFor*). اما، به طور کلی، یک چیز خوب است.

چگونه با pywinauto شروع کنیم

اولین قدم این است که خود را به یک بازرس شی GUI (چیزی که ابزار جاسوسی نامیده می شود) مجهز کنید. این به مطالعه برنامه از داخل کمک می کند: سلسله مراتب عناصر چگونه مرتب شده است، چه ویژگی هایی در دسترس است. معروف ترین بازرسان اشیا عبارتند از:

  • جاسوس ++ - همراه ویژوال استودیو، از جمله نسخه Express یا Community Edition. از Win32 API استفاده می کند. همچنین به عنوان یک کلون شناخته می شود اطلاعات پنجره AutoIt.
  • Inspect.exe - گنجانده شده است در Windows SDK. اگر آن را نصب کرده‌اید، پس روی ۶۴ بیتی است Windows میتونید توی پوشه پیداش کنید C:Program Files (x86)Windows Kits<winver>binx64. در خود بازرس، باید حالت را انتخاب کنید اتوماسیون رابط کاربری به جای MS AA (دسترسی فعال، جد اتوماسیون UI).

پس از روشن کردن برنامه از طریق و از طریق، ما باطن مورد استفاده را انتخاب می کنیم. در هنگام ایجاد شیء Application کافی است نام Backend را مشخص کنید.

  • backend = "win32" - در حالی که به طور پیش فرض استفاده می شود، با MFC، WTL، VB6 و سایر برنامه های قدیمی به خوبی کار می کند.
  • backend = "uia" - بک‌اند جدید برای اتوماسیون رابط کاربری مایکروسافت: کاملاً با WPF و WinForms کار می‌کند؛ همچنین برای دلفی و ... نیز مناسب است. Windows برنامه‌های فروشگاه؛ با Qt5 و برخی از برنامه‌های جاوا کار می‌کند. و به طور کلی، اگر Inspect.exe عناصر و ویژگی‌های آنها را ببیند، این backend مناسب است. اساساً، اکثر مرورگرها از UI Automation نیز پشتیبانی می‌کنند (موزیلا به طور پیش‌فرض و کروم هنگام اجرا نیاز به یک سوئیچ خط فرمان دارد). --force-renderer-accessibilityبرای دیدن موارد در صفحات در Inspect.exe). البته رقابت با سلنیوم در این زمینه به سختی امکان پذیر است. فقط راه دیگری برای کار با مرورگر (ممکن است برای سناریوی محصول متقابل مفید باشد).

نقاط ورودی برای اتوماسیون

این برنامه به خوبی مورد بررسی قرار گرفته است. زمان آن رسیده است که یک شی Application ایجاد کنید و آن را اجرا کنید یا به یکی از مواردی که قبلا در حال اجرا است متصل کنید. این فقط یک کلون از کلاس استاندارد نیست subprocess.Popen، که یک شی مقدماتی است که تمام اقدامات شما را به مرزهای فرآیند محدود می کند. اگر چندین نمونه از برنامه در حال اجرا هستند و نمی خواهید بقیه را لمس کنید، بسیار مفید است.

from pywinauto.application import Application
app = Application(backend="uia").start('notepad.exe')

# Опишем окно, которое хотим найти в процессе Notepad.exe
dlg_spec = app.UntitledNotepad
# ждем пока окно реально появится
actionable_dlg = dlg_spec.wait('visible')

اگر می خواهید چندین برنامه را همزمان مدیریت کنید، کلاس به شما کمک می کند Desktop. به عنوان مثال، در ماشین حساب در Win10، سلسله مراتب عناصر در چندین فرآیند پخش شده است (نه تنها calc.exe). پس شیئی نیست Desktop کافی نیست.

from subprocess import Popen
from pywinauto import Desktop

Popen('calc.exe', shell=True)
dlg = Desktop(backend="uia").Calculator
dlg.wait('visible')

شی ریشه (Application یا Desktop) تنها جایی است که باید backend را مشخص کنید. هر چیز دیگری به طور شفاف در مفهوم "مشخصات-> لفاف" قرار می گیرد که بعداً مورد بحث قرار خواهد گرفت.

مشخصات پنجره/عنصر

این مفهوم اصلی است که رابط pywinauto بر اساس آن ساخته شده است. می‌توانید پنجره/عنصر را تقریباً یا با جزئیات بیشتر توصیف کنید، حتی اگر هنوز وجود نداشته باشد یا قبلاً بسته شده باشد. مشخصات پنجره (object مشخصات پنجره) معیارهایی را ذخیره می کند که بر اساس آنها باید یک پنجره یا عنصر واقعی را جستجو کنید.

نمونه ای از مشخصات دقیق پنجره:

>>> dlg_spec = app.window(title='Untitled - Notepad')

>>> dlg_spec
<pywinauto.application.WindowSpecification object at 0x0568B790>

>>> dlg_spec.wrapper_object()
<pywinauto.controls.win32_controls.DialogWrapper object at 0x05639B70>

جستجوی خود پنجره با فراخوانی متد انجام می شود .wrapper_object(). مقداری "wrapper" را برای یک پنجره/عنصر واقعی یا پرتاب برمی گرداند ElementNotFoundError (گاهی ElementAmbiguousError، اگر چندین عنصر پیدا شد، یعنی باید معیارهای جستجو را اصلاح کنید). این "wrapper" از قبل می داند که چگونه برخی از اقدامات را با عنصر انجام دهد یا داده ها را از آن دریافت کند.

پایتون می تواند تماس را مخفی کند .wrapper_object()، تا کد نهایی کوتاهتر شود. توصیه می کنیم از آن فقط برای اهداف اشکال زدایی استفاده کنید. دو خط بعدی دقیقاً همین کار را انجام می دهند:

dlg_spec.wrapper_object().minimize() # debugging
dlg_spec.minimize() # production

معیارهای جستجوی مختلفی برای مشخصات پنجره وجود دارد. در اینجا فقط چند نمونه آورده شده است:

# могут иметь несколько уровней
app.window(title_re='.* - Notepad$').window(class_name='Edit')

# можно комбинировать критерии (как AND) и не ограничиваться одним процессом приложения
dlg = Desktop(backend="uia").Calculator
dlg.window(auto_id='num8Button', control_type='Button')

لیست تمام معیارهای ممکن در پایه های تابع است pywinauto.findwindows.find_elements(…).

جادوی دسترسی با ویژگی و کلید

پایتون ایجاد مشخصات پنجره و شناسایی ویژگی های شی به صورت پویا را آسان می کند (داخلی لغو شده است __getattribute__). البته، همان محدودیت هایی برای نام ویژگی اعمال می شود که برای نام هر متغیری اعمال می شود (شما نمی توانید فاصله، کاما و سایر کاراکترهای خاص را وارد کنید). خوشبختانه، pywinauto از الگوریتم جستجوی به اصطلاح "بهترین مطابقت" استفاده می کند که در برابر اشتباهات تایپی و تغییرات جزئی مقاوم است.

app.UntitledNotepad
# то же самое, что
app.window(best_match='UntitledNotepad')

اگر همچنان به رشته‌های یونیکد (مثلاً برای زبان روسی)، فاصله‌ها و غیره نیاز دارید، می‌توانید با کلید دسترسی داشته باشید (مثل اینکه یک فرهنگ لغت معمولی است):

app['Untitled - Notepad']
# то же самое, что
app.window(best_match='Untitled - Notepad')

پنج قانون برای نام های جادویی

چگونه می توان نام های جادویی مرجع را پیدا کرد؟ آنهایی که قبل از جستجو به عنصر اختصاص داده شده اند. اگر نامی را مشخص کرده باشید که به اندازه کافی شبیه الگو باشد، آن عنصر پیدا خواهد شد.

  1. بر اساس عنوان (متن، نام): app.Properties.OK.click()
  2. بر اساس متن و نوع عنصر: app.Properties.OKButton.click()
  3. بر اساس نوع و تعداد: app.Properties.Button3.click() (اسامی Button0 и Button1 محدود به اولین عنصر یافت شده، Button2 - به دوم، و سپس به ترتیب - از نظر تاریخی اتفاق افتاد)
  4. بر اساس متن ایستا (چپ یا بالا) و بر اساس نوع: app.OpenDialog.FileNameEdit.set_text("") (مفید برای عناصر با متن پویا)
  5. بر اساس نوع و متن داخل: app.Properties.TabControlSharing.select("General")

معمولاً دو یا سه قانون به طور همزمان اعمال می شود، به ندرت بیشتر. برای بررسی اینکه کدام نام های خاص برای هر عنصر موجود است، می توانید از روش استفاده کنید print_control_identifiers(). می تواند درختی از عناصر را هم روی صفحه و هم در یک فایل چاپ کند. برای هر عنصر، نام جادویی مرجع آن چاپ می شود. همچنین می‌توانید مشخصات دقیق‌تر عناصر فرزند را از آنجا کپی-پیست کنید. نتیجه در اسکریپت به صورت زیر خواهد بود:

app.Properties.child_window(data-gt-translate-attributes='["title"]' title="Contains:", auto_id="13087", control_type="Edit")

خود درخت عناصر معمولاً یک پاپوش نسبتاً بزرگ است.

>>> app.Properties.print_control_identifiers()

Control Identifiers:

Dialog - 'Windows NT Properties'    (L688, T518, R1065, B1006)
[u'Windows NT PropertiesDialog', u'Dialog', u'Windows NT Properties']
child_window(data-gt-translate-attributes='["title"]' title="Windows NT Properties", control_type="Window")
   |
   | Image - ''    (L717, T589, R749, B622)
   | [u'', u'0', u'Image1', u'Image0', 'Image', u'1']
   | child_window(auto_id="13057", control_type="Image")
   |
   | Image - ''    (L717, T630, R1035, B632)
   | ['Image2', u'2']
   | child_window(auto_id="13095", control_type="Image")
   |
   | Edit - 'Folder name:'    (L790, T596, R1036, B619)
   | [u'3', 'Edit', u'Edit1', u'Edit0']
   | child_window(data-gt-translate-attributes='["title"]' title="Folder name:", auto_id="13156", control_type="Edit")
   |
   | Static - 'Type:'    (L717, T643, R780, B658)
   | [u'Type:Static', u'Static', u'Static1', u'Static0', u'Type:']
   | child_window(data-gt-translate-attributes='["title"]' title="Type:", auto_id="13080", control_type="Text")
   |
   | Edit - 'Type:'    (L790, T643, R1036, B666)
   | [u'4', 'Edit2', u'Type:Edit']
   | child_window(data-gt-translate-attributes='["title"]' title="Type:", auto_id="13059", control_type="Edit")
   |
   | Static - 'Location:'    (L717, T669, R780, B684)
   | [u'Location:Static', u'Location:', u'Static2']
   | child_window(data-gt-translate-attributes='["title"]' title="Location:", auto_id="13089", control_type="Text")
   |
   | Edit - 'Location:'    (L790, T669, R1036, B692)
   | ['Edit3', u'Location:Edit', u'5']
   | child_window(data-gt-translate-attributes='["title"]' title="Location:", auto_id="13065", control_type="Edit")
   |
   | Static - 'Size:'    (L717, T695, R780, B710)
   | [u'Size:Static', u'Size:', u'Static3']
   | child_window(data-gt-translate-attributes='["title"]' title="Size:", auto_id="13081", control_type="Text")
   |
   | Edit - 'Size:'    (L790, T695, R1036, B718)
   | ['Edit4', u'6', u'Size:Edit']
   | child_window(data-gt-translate-attributes='["title"]' title="Size:", auto_id="13064", control_type="Edit")
   |
   | Static - 'Size on disk:'    (L717, T721, R780, B736)
   | [u'Size on disk:', u'Size on disk:Static', u'Static4']
   | child_window(data-gt-translate-attributes='["title"]' title="Size on disk:", auto_id="13107", control_type="Text")
   |
   | Edit - 'Size on disk:'    (L790, T721, R1036, B744)
   | ['Edit5', u'7', u'Size on disk:Edit']
   | child_window(data-gt-translate-attributes='["title"]' title="Size on disk:", auto_id="13106", control_type="Edit")
   |
   | Static - 'Contains:'    (L717, T747, R780, B762)
   | [u'Contains:1', u'Contains:0', u'Contains:Static', u'Static5', u'Contains:']
   | child_window(data-gt-translate-attributes='["title"]' title="Contains:", auto_id="13088", control_type="Text")
   |
   | Edit - 'Contains:'    (L790, T747, R1036, B770)
   | [u'8', 'Edit6', u'Contains:Edit']
   | child_window(data-gt-translate-attributes='["title"]' title="Contains:", auto_id="13087", control_type="Edit")
   |
   | Image - 'Contains:'    (L717, T773, R1035, B775)
   | [u'Contains:Image', 'Image3', u'Contains:2']
   | child_window(data-gt-translate-attributes='["title"]' title="Contains:", auto_id="13096", control_type="Image")
   |
   | Static - 'Created:'    (L717, T786, R780, B801)
   | [u'Created:', u'Created:Static', u'Static6', u'Created:1', u'Created:0']
   | child_window(data-gt-translate-attributes='["title"]' title="Created:", auto_id="13092", control_type="Text")
   |
   | Edit - 'Created:'    (L790, T786, R1036, B809)
   | [u'Created:Edit', 'Edit7', u'9']
   | child_window(data-gt-translate-attributes='["title"]' title="Created:", auto_id="13072", control_type="Edit")
   |
   | Image - 'Created:'    (L717, T812, R1035, B814)
   | [u'Created:Image', 'Image4', u'Created:2']
   | child_window(data-gt-translate-attributes='["title"]' title="Created:", auto_id="13097", control_type="Image")
   |
   | Static - 'Attributes:'    (L717, T825, R780, B840)
   | [u'Attributes:Static', u'Static7', u'Attributes:']
   | child_window(data-gt-translate-attributes='["title"]' title="Attributes:", auto_id="13091", control_type="Text")
   |
   | CheckBox - 'Read-only (Only applies to files in folder)'    (L790, T825, R1035, B841)
   | [u'CheckBox0', u'CheckBox1', 'CheckBox', u'Read-only (Only applies to files in folder)CheckBox', u'Read-only (Only applies to files in folder)']
   | child_window(data-gt-translate-attributes='["title"]' title="Read-only (Only applies to files in folder)", auto_id="13075", control_type="CheckBox")
   |
   | CheckBox - 'Hidden'    (L790, T848, R865, B864)
   | ['CheckBox2', u'HiddenCheckBox', u'Hidden']
   | child_window(data-gt-translate-attributes='["title"]' title="Hidden", auto_id="13076", control_type="CheckBox")
   |
   | Button - 'Advanced...'    (L930, T845, R1035, B868)
   | [u'Advanced...', u'Advanced...Button', 'Button', u'Button1', u'Button0']
   | child_window(data-gt-translate-attributes='["title"]' title="Advanced...", auto_id="13154", control_type="Button")
   |
   | Button - 'OK'    (L814, T968, R889, B991)
   | ['Button2', u'OK', u'OKButton']
   | child_window(data-gt-translate-attributes='["title"]' title="OK", auto_id="1", control_type="Button")
   |
   | Button - 'Cancel'    (L895, T968, R970, B991)
   | ['Button3', u'CancelButton', u'Cancel']
   | child_window(data-gt-translate-attributes='["title"]' title="Cancel", auto_id="2", control_type="Button")
   |
   | Button - 'Apply'    (L976, T968, R1051, B991)
   | ['Button4', u'ApplyButton', u'Apply']
   | child_window(data-gt-translate-attributes='["title"]' title="Apply", auto_id="12321", control_type="Button")
   |
   | TabControl - ''    (L702, T556, R1051, B962)
   | [u'10', u'TabControlSharing', u'TabControlPrevious Versions', u'TabControlSecurity', u'TabControl', u'TabControlCustomize']
   | child_window(auto_id="12320", control_type="Tab")
   |    |
   |    | TabItem - 'General'    (L704, T558, R753, B576)
   |    | [u'GeneralTabItem', 'TabItem', u'General', u'TabItem0', u'TabItem1']
   |    | child_window(data-gt-translate-attributes='["title"]' title="General", control_type="TabItem")
   |    |
   |    | TabItem - 'Sharing'    (L753, T558, R801, B576)
   |    | [u'Sharing', u'SharingTabItem', 'TabItem2']
   |    | child_window(data-gt-translate-attributes='["title"]' title="Sharing", control_type="TabItem")
   |    |
   |    | TabItem - 'Security'    (L801, T558, R851, B576)
   |    | [u'Security', 'TabItem3', u'SecurityTabItem']
   |    | child_window(data-gt-translate-attributes='["title"]' title="Security", control_type="TabItem")
   |    |
   |    | TabItem - 'Previous Versions'    (L851, T558, R947, B576)
   |    | [u'Previous VersionsTabItem', u'Previous Versions', 'TabItem4']
   |    | child_window(data-gt-translate-attributes='["title"]' title="Previous Versions", control_type="TabItem")
   |    |
   |    | TabItem - 'Customize'    (L947, T558, R1007, B576)
   |    | [u'CustomizeTabItem', 'TabItem5', u'Customize']
   |    | child_window(data-gt-translate-attributes='["title"]' title="Customize", control_type="TabItem")
   |
   | TitleBar - 'None'    (L712, T521, R1057, B549)
   | ['TitleBar', u'11']
   |    |
   |    | Menu - 'System'    (L696, T526, R718, B548)
   |    | [u'System0', u'System', u'System1', u'Menu', u'SystemMenu']
   |    | child_window(data-gt-translate-attributes='["title"]' title="System", auto_id="MenuBar", control_type="MenuBar")
   |    |    |
   |    |    | MenuItem - 'System'    (L696, T526, R718, B548)
   |    |    | [u'System2', u'MenuItem', u'SystemMenuItem']
   |    |    | child_window(data-gt-translate-attributes='["title"]' title="System", control_type="MenuItem")
   |    |
   |    | Button - 'Close'    (L1024, T519, R1058, B549)
   |    | [u'CloseButton', u'Close', 'Button5']
   |    | child_window(data-gt-translate-attributes='["title"]' title="Close", control_type="Button")

در برخی موارد، چاپ کل درخت می تواند کند شود (به عنوان مثال، در iTunes به اندازه سه هزار مورد در یک برگه وجود دارد!)، اما می توانید از پارامتر استفاده کنید. depth (عمق): depth=1 - خود عنصر depth=2 - فقط بچه های فوری و غیره. همچنین در هنگام ایجاد می توان آن را در مشخصات مشخص کرد child_window.

نمونه

ما دائماً در حال پر کردن هستیم لیست نمونه های موجود در مخزن. از موارد تازه، شایان ذکر است که اتوماسیون تحلیلگر شبکه WireShark (این نمونه خوبی از یک برنامه Qt5 است؛ اگرچه این کار را می توان بدون رابط کاربری گرافیکی حل کرد، زیرا وجود دارد scapy.Sniffer از بسته پایتون پوسته پوسته شدن). همچنین نمونه ای از اتوماسیون MS Paint با نوار ابزار Ribbon آن وجود دارد.

مثال عالی دیگری که توسط شاگرد من نوشته شده است: کشیدن فایل از explorer.exe به صفحه کروم برای درایو گوگل (کمی بعد به مخزن اصلی منتقل می شود).

و البته نمونه ای از اشتراک در رویدادهای صفحه کلید (کلیدهای داغ) و ماوس:
hook_and_listen.py.

تقدیر و تشکر

تشکر ویژه از کسانی که به طور مداوم به توسعه پروژه کمک می کنند. برای من و ولنتاین این یک سرگرمی مداوم است. دو نفر از دانشجویان من از UNN اخیراً مدرک لیسانس خود را در این موضوع به پایان رساندند. الکساندر کمک بزرگی به پشتیبانی از MS UI Automation کرد و اخیراً شروع به ساخت یک تولید کننده کد خودکار بر اساس اصل "Record-play" بر اساس ویژگی های متن (این سخت ترین ویژگی است) تا کنون فقط برای باطن "uia" آغاز کرده است. ایوان در حال توسعه یک بک‌اند جدید برای Linux بر اساس AT-SPI (ماژول‌ها) mouse и keyboard مستقر python-xlib - در حال حاضر در نسخه های 0.6.x).

از آنجایی که من مدت زیادی است که در پایتون یک دوره ویژه در زمینه اتوماسیون تدریس می کنم، برخی از دانشجویان کارشناسی ارشد تکالیف خود را انجام می دهند و ویژگی های کوچک یا نمونه هایی از اتوماسیون را پیاده سازی می کنند. برخی از چیزهای کلیدی در مرحله تحقیق نیز زمانی توسط دانش آموزان کشف شد. اگرچه گاهی اوقات باید به شدت بر کیفیت کد نظارت داشت. تحلیلگرهای استاتیک (QuantifiedCode، Codacy و Landscape) و تست‌های خودکار در فضای ابری (سرویس AppVeyor) با پوشش کد حدود 95 درصد به این امر کمک زیادی می‌کنند.

همچنین از همه کسانی که بازخورد می‌گذارند، اشکالات را شروع می‌کنند و درخواست‌های کشش می‌فرستند، تشکر می‌کنیم!

منابع اضافی

ما سوالات را دنبال می کنیم روی StackOverflow تگ کنید (اخیرا ظاهر شد برچسب در نسخه روسی SO) و با کلمه کلیدی در توستر. وجود دارد چت روسی در Gitter.

ما هر ماه به روز می کنیم رتبه بندی کتابخانه های منبع باز برای تست رابط کاربری گرافیکی. از نظر تعداد ستاره‌های گیت‌هاب، فقط Autohotkey (آنها جامعه بسیار بزرگ و سابقه طولانی دارند) و PyAutoGUI (بیشتر به دلیل محبوبیت کتاب‌های نویسنده آن Al Sweigart: "Automate the Boring Stuff with Python" و دیگران) سریعتر رشد می کنند.

منبع: www.habr.com

خرید هاست قابل اعتماد برای سایت های دارای حفاظت DDoS، سرورهای VPS VDS 🔥 خرید هاستینگ معتبر با محافظت در برابر حملات DDoS، سرورهای VPS و VDS | ProHoster