<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          讓 dogtail 識(shí)別 UI 中的元素

          共 6072字,需瀏覽 13分鐘

           ·

          2020-10-10 22:55


          在《利用 dogtail 快速進(jìn)行 GUI 自動(dòng)化測(cè)試》一節(jié)中,我們實(shí)現(xiàn)了一個(gè)最基礎(chǔ)的自動(dòng)化測(cè)試程序 - 模擬鼠標(biāo)點(diǎn)擊按鈕?,F(xiàn)在是時(shí)候更進(jìn)一步了,開(kāi)始訪問(wèn) UI 元素中的信息。


          假設(shè),要獲取按鈕上的文本(例如:用于斷言測(cè)試),修改之前的腳本:


          #!/usr/bin/env?python3
          #?-*-?coding:?utf-8?-*-

          from?dogtail.tree?import?*
          import?time

          #?獲取應(yīng)用程序(根據(jù)程序名稱(chēng)查找)
          app?=?root.application(appName="Sample02",?description="/home/waleon/workspace/demos/build-Samples-unknown-Debug/Sample02/Sample02")

          #?獲取按鈕(根據(jù)?accessibleName?遞歸查找)
          button?=?app.child('button')

          #?獲取并打印按鈕上的文本
          print('text:',?button.text)

          #?模擬鼠標(biāo)點(diǎn)擊
          for?i?in?range(3):
          ????button.click()
          ????sleep(1)


          運(yùn)行之后,你會(huì)發(fā)現(xiàn) print() 打印的永遠(yuǎn)是 None。這是什么情況,按鈕上分明有內(nèi)容“test”,但為什么打印不出來(lái)?



          這是因?yàn)?,按鈕元素的信息沒(méi)有對(duì)外公開(kāi),以至于 dogtail 無(wú)法識(shí)別出來(lái)!


          不妨用 sniff 工具查看一下,定位到我們的程序并選擇該按鈕,點(diǎn)擊底下的“Text”選項(xiàng):



          沒(méi)有任何內(nèi)容,這就說(shuō)明按鈕的文本是不可訪問(wèn)的。



          1

          實(shí)現(xiàn)可訪問(wèn)性


          要為 UI 元素提供可訪問(wèn)性支持,需要使用 Qt 的 Accessibility 技術(shù)。即應(yīng)實(shí)現(xiàn) QAccessibleInterface,分發(fā)的形式有兩種:


          • 實(shí)現(xiàn)可訪問(wèn)的插件:子類(lèi)化 QAccessiblePlugin,重新實(shí)現(xiàn)純虛函數(shù) create(),并使用 Q_PLUGIN_METADATA() 宏導(dǎo)出該類(lèi)(如果想在運(yùn)行時(shí)按需加載,則將接口作為插件分發(fā)比較方便)。

          • 將接口編譯到應(yīng)用程序中:使用接口工廠 - QAccessible::InterfaceFactory(如果是靜態(tài)鏈接,或不想增加插件的復(fù)雜性,則推薦該方式)。


          下面以接口工廠為例,來(lái)實(shí)現(xiàn)對(duì)按鈕文本的可訪問(wèn)性(要對(duì)文本進(jìn)行操作,需要用到 QAccessibleTextInterface):


          #ifndef?ACCESSIBLE_BUTTON_H
          #define?ACCESSIBLE_BUTTON_H

          #include?
          #include?
          #include?

          class?AccessibleButton?:?public?QAccessibleWidget
          ????????,?public?QAccessibleTextInterface
          {
          public:
          ????explicit?AccessibleButton(QPushButton?*button);
          ????~AccessibleButton();

          ????void?*interface_cast(QAccessible::InterfaceType?t)?Q_DECL_OVERRIDE;
          ????QString?text(QAccessible::Text?t)?const?Q_DECL_OVERRIDE;

          ????QString?text(int?startOffset,?int?endOffset)?const?Q_DECL_OVERRIDE;
          ????void?selection(int?selectionIndex,?int?*startOffset,?int?*endOffset)?const?Q_DECL_OVERRIDE;
          ????int?selectionCount()?const?Q_DECL_OVERRIDE;
          ????void?addSelection(int?startOffset,?int?endOffset)?Q_DECL_OVERRIDE;
          ????void?removeSelection(int?selectionIndex)?Q_DECL_OVERRIDE;
          ????void?setSelection(int?selectionIndex,?int?startOffset,?int?endOffset)?Q_DECL_OVERRIDE;
          ????int?cursorPosition()?const?Q_DECL_OVERRIDE;
          ????void?setCursorPosition(int?position)?Q_DECL_OVERRIDE;
          ????int?characterCount()?const?Q_DECL_OVERRIDE;
          ????QRect?characterRect(int?offset)?const?Q_DECL_OVERRIDE;
          ????int?offsetAtPoint(const?QPoint?&point)?const?Q_DECL_OVERRIDE;
          ????void?scrollToSubstring(int?startIndex,?int?endIndex)?Q_DECL_OVERRIDE;
          ????QString?attributes(int?offset,?int?*startOffset,?int?*endOffset)?const?Q_DECL_OVERRIDE;

          private:
          ????QPushButton?*m_button;
          };

          #endif?//?ACCESSIBLE_BUTTON_H


          由于訪問(wèn)的是按鈕的文本,所以重點(diǎn)關(guān)注 interface_cast()、text() 即可,其他接口可以“忽略”:


          #include?"accessiblebutton.h"
          #include?

          AccessibleButton::AccessibleButton(QPushButton?*button)
          ????:?QAccessibleWidget(button)
          ????,?m_button(button)
          {

          }

          AccessibleButton::~AccessibleButton()
          {

          }

          void?*AccessibleButton::interface_cast(QAccessible::InterfaceType?t)
          {
          ????switch?(t)?{
          ????case?QAccessible::ActionInterface:
          ????????return?static_cast(this);
          ????case?QAccessible::TextInterface:
          ????????return?static_cast(this);
          ????default:
          ????????return?nullptr;
          ????}
          }

          QString?AccessibleButton::text(QAccessible::Text?t)?const
          {
          ????switch?(t)?{
          ????case?QAccessible::Name:
          ????????return?m_button->accessibleName();
          ????case?QAccessible::Description:
          ????????return?m_button->accessibleDescription();
          ????default:
          ????????return?QString();
          ????}
          }

          QString?AccessibleButton::text(int?startOffset,?int?endOffset)?const
          {
          ????Q_UNUSED(startOffset)
          ????Q_UNUSED(endOffset)

          ????return?m_button->text();
          }

          void?AccessibleButton::selection(int?selectionIndex,?int?*startOffset,?int?*endOffset)?const
          {
          ????Q_UNUSED(selectionIndex)
          ????Q_UNUSED(startOffset)
          ????Q_UNUSED(endOffset)
          }

          int?AccessibleButton::selectionCount()?const
          {
          ????return?0;
          }

          void?AccessibleButton::addSelection(int?startOffset,?int?endOffset)
          {
          ????Q_UNUSED(startOffset)
          ????Q_UNUSED(endOffset)
          }

          void?AccessibleButton::removeSelection(int?selectionIndex)
          {
          ?????Q_UNUSED(selectionIndex)
          }

          void?AccessibleButton::setSelection(int?selectionIndex,?int?startOffset,?int?endOffset)
          {
          ????Q_UNUSED(selectionIndex)
          ????Q_UNUSED(startOffset)
          ????Q_UNUSED(endOffset)
          }

          int?AccessibleButton::cursorPosition()?const
          {
          ????return?0;
          }

          void?AccessibleButton::setCursorPosition(int?position)
          {
          ????Q_UNUSED(position)
          }

          int?AccessibleButton::characterCount()?const
          {
          ????return?0;
          }

          QRect?AccessibleButton::characterRect(int?offset)?const
          {
          ????Q_UNUSED(offset)

          ????return?QRect();
          }

          int?AccessibleButton::offsetAtPoint(const?QPoint?&point)?const
          {
          ????Q_UNUSED(point)

          ????return?0;
          }

          void?AccessibleButton::scrollToSubstring(int?startIndex,?int?endIndex)
          {
          ????Q_UNUSED(startIndex)
          ????Q_UNUSED(endIndex)
          }

          QString?AccessibleButton::attributes(int?offset,?int?*startOffset,?int?*endOffset)?const
          {
          ????Q_UNUSED(offset)
          ????Q_UNUSED(startOffset)
          ????Q_UNUSED(endOffset)

          ????return?QString();
          }



          2

          實(shí)現(xiàn)接口工廠


          工廠是一個(gè)函數(shù)指針,其簽名如下:


          typedef?QAccessibleInterface*?myFactoryFunction(const?QString?&key,?QObject?*);


          該函數(shù)接收一個(gè) QString 和一個(gè) QObject 指針,其中 QString 是標(biāo)識(shí)接口的鍵。QObject 用于傳遞到 QAccessibleInterface,以便它可以保存對(duì)其的引用。


          注意:如果 key 和 QObject 沒(méi)有對(duì)應(yīng)的 QAccessibleInterface,將返回一個(gè)空指針。


          來(lái)看一下我們要實(shí)現(xiàn)的工廠示例:


          //?接口工廠
          QAccessibleInterface?*accessibleFactory(const?QString?&classname,?QObject?*object)
          {
          ????QAccessibleInterface?*interface?=?nullptr;

          ????if?(classname?==?"QPushButton"?&&?object?&&?object->isWidgetType())
          ????????interface?=?new?AccessibleButton(static_cast(object));

          ????return?interface;
          }



          3

          安裝工廠


          完成之后,需要使用 QAccessible::installFactory 對(duì)上述工廠進(jìn)行安裝:


          int?main(int?argc,?char?*argv[])
          {
          ????QApplication?a(argc,?argv);

          ????//?安裝工廠
          ????QAccessible::installFactory(accessibleFactory);

          ????QPushButton?button("test");
          ????QObject::connect(&button,?&QPushButton::clicked,?[&]()?{
          ????????static?int?index?=?0;
          ????????index++;
          ????????button.setText(QString("click?%1").arg(index));
          ????});

          ????//?將被輔助技術(shù)識(shí)別
          ????button.setAccessibleName("button");
          ????button.setAccessibleDescription("this?is?a?simple?button");

          ????button.resize(300,?200);
          ????button.show();

          ????return?a.exec();
          }



          4

          執(zhí)行自動(dòng)化


          運(yùn)行程序,再使用 sniff 工具查看一下,這時(shí)已經(jīng)可以檢測(cè)出按鈕的文本了:



          然后重新運(yùn)行下自動(dòng)化腳本,文本也可以正常打印了:



          恭喜,這說(shuō)明 dogtail 已經(jīng)可以成功識(shí)別 UI 中的元素了。


          最后,在自動(dòng)化測(cè)試的過(guò)程中,有可能還會(huì)有其他元素的信息無(wú)法識(shí)別,參考上述方式即可。


          ·END·



          謝謝您的支持,需要任何資源,只需要在公眾號(hào)后臺(tái)回復(fù)對(duì)應(yīng)數(shù)字即可:
          01:dotnet
          02:java
          03:android
          04:C++
          05:qt
          06:react

          沒(méi)有的資源,也可以給我留言,我會(huì)去尋找的哦。
          另:大部分資源可在我的網(wǎng)站搜索哦:https://dotnet9.com
          瀏覽 48
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  久久三级电影 | 亚洲天堂网导航 | 国产黄色一区二区 | 一本色道久草在线 | 天天撸日日干 |