Qt对象操作API

对象操作API的方法分为两类,操作和属性。操作对控件做实际的动作,属性是获得控件的运行时属性。因为控件访问是异步的,所以获取属性也是以方法的形式,即调用时需要加上括号”()”,并且返回目标值的Promise对象——Promise<T>,需要在调用前加上await关键字来获取Promise中的结果。

API语法说明

Qt的API文档建立在leanpro.qt的类型文件上(比如可以进入leanpro.qt.d.ts类型定义文件中查看API的调用方式),因此采用的是TypeScript语法,下面会根据阅读文档所需知识进行简单的介绍,如果希望进一步了解,可以前往TypeScript中文介绍了解更多相关语法。

首先是当前类的名称,在这里是IQtControl,全称可以理解为Interface of Qt Control,代表广义的Qt控件的接口定义。

其次是基类,代表当前类扩展自哪个基类,扩展代表会继承基类中的属性和方法。这里扩展自容器类IQtContainer,容器包含了各种获取对象API,这样的扩展可以实现链式的对象获取。

比如获取“Table OverView”窗口中的”sheet1”表格里的”Family”单元格的完整调用可以写作:

JavaScript
Python
model.getWindow("Table Overview").getTable("sheet1").getTableItem("Family");
model.getWindow("Table Overview").getTable("sheet1").getTableItem("Family")
CukeTest在实际生成控件调用代码时会进行优化,所以我们平时不会看到这么长的调用。

最后就是当前类的操作方法和属性方法,操作方法和属性方法之间空了一行表示区分。()中代表了传入的参数个数,参数的格式为[argName]:[argType],如果:前有?,代表该参数有缺省值,即使为空也不影响运行。方法的尾部(即最后一个:后面)代表方法的返回值,Promise<T>表示该方法是一个异步方法,只有T则代表是同步方法。

这里的T表示类型名称,比如voidstringnumber等。

各个类的关系

在CukeTest中,几个类之间的扩展关系大致如下:

  1. 最底层基类,容器类IQtContainer,包含获取自动化对象的方法;
  2. 第二层基类,基本控件类IQtControl,包含了各个控件通用的操作和属性方法;
  3. 具体控件类,有IQWindowIQLabel...等等一系列具体Qt控件,除了扩展而来的方法外,大部分控件都包含自己特有的操作和属性方法,来实现针对不同控件的自动化。

通用的API

不同类型的对象操作有不同的操作和属性。它们都有一些共用的操作和属性,无论是哪个控件都可以调用的,如下:

JavaScript
Python
export interface IQtControl extends IQtContainer {
    // 通用操作方法
    click(x?: number, y?: number, mousekey?: number): Promise<void>;
    dblClick(x?: number, y?: number, mousekey?: number): Promise<void>;
    moveMouse(x?: number, y?: number, seconds?: number): Promise<void>;
    wheel(value: number): Promise<void>;
    drag(x?: number, y?: number, mouseKey: number): Promise<void>;
    drop(x?: number, y?: number, {seconds?: number, mouseKey?: number}): Promise<void>;
    pressKeys(keys: string, options?: PressKeysOptions | number): Promise<void>;
    exists(seconds: number): Promise<boolean>;
    highlight(duration: number);
    takeScreenshot(filePath?: string): Promise<void | string>;
    invoke(methodName: string, args?: (string | number | boolean)[]): Promise<object>;
    all(): Promise<IQtControl[]>;
    
    // 通用属性方法
    rect(): Promise<Rect>;
    winId(): Promise<number>;
    enabled(): Promise<boolean>;
    visible(): Promise<boolean>;
    modelProperties(): {[x: string]: any};
    modelImage(options?: {encoding: 'buffer' | 'base64'}): Promise<Buffer | string>;  //base64 is the default
    allProperties(): Promise<object>
    property(propertyName: string): Promise<any>
    waitProperty(propertyName: QtPropertyIds, value: string | number | boolean, timeOutSeconds: number): Promise<boolean>
    checkProperty(propertyName: QtPropertyIds, expectedValue: string | number | boolean | RegExp, message: string): Promise<void>;
    checkImage(options?: CompareOptions): Promise<void>;

    // 通用导航方法
    firstChild(controlType?: QtControlType): Promise<IQtControl>;
    lastChild(controlType?: QtControlType): Promise<IQtControl>;
    next(controlType?: QtControlType): Promise<IQtControl>;
    previous(controlType?: QtControlType): Promise<IQtControl>;
    parent(): Promise<IQtControl>;

    // 通用控件搜索方法
    findControls(...conditions: ConditionFilter[]): Promise<IQtControl[]>; 
}
class QtControl(QtContainer):
	def click(x: Optional[int]=None, y: Optional[int]=None, mousekey: Optional[int]=None) -> None
	def dblClick(x: Optional[int]=None, y: Optional[int]=None, mousekey: Optional[int]=None) -> None
	def exists(seconds: Optional[int]=None) -> bool
	def moveMouse(x: Optional[int]=None, y: Optional[int]=None, seconds: Optional[int]=None) -> None
	def wheel(value: int) -> None
	def drop(x: Optional[int]=None, y: Optional[int]=None, seconds: Optional[int]=None) -> None
	def drag(x: Optional[int]=None, y: Optional[int]=None) -> None
	def pressKeys(keys: str, opt: Optional[Union[int, PressKeysOptions]]=None) -> None
	def highlight(duration: Optional[int]=None) -> None
	def takeScreenshot(filePath: Optional[str]=None) -> Union[str, None]
  def invoke(methodName: str, args: Optional[List[str]]=None) -> "str"
  def all() -> List['QtControl']
	def rect() -> "Rect"
	def winId() -> int
	def enabled() -> bool
	def visible() -> bool
	def modelProperties(all: Optional[bool]=None) -> TypedDict
	def modelImage(encoding: Optional[str]=None) -> Union[str, bytearray]
	def allProperties() -> TypedDict
	def property(propertyIds: str) -> Union[str, int, bool, TypedDict]
	def waitProperty(propertyName: str, value: Union[str, int, bool], timeOutSeconds: Optional[int]=None) -> bool
  def checkProperty(propertyName: str, expectedValue: Union[str, int, bool], optionsOrMessage: Optional[Union[str, TypedDict]]=None) -> None:
	def firstChild(controlType: Optional[str]=None) -> "QtControl"
	def lastChild(controlType: Optional[str]=None) -> "QtControl"
	def next(controlType: Optional[str]=None) -> "QtControl"
	def previous(controlType: Optional[str]=None) -> "QtControl"
	def parent() -> "QtControl"
	def findControls(*conditions: List[Union[str, Dict]]) -> List[QtControl]

操作方法

click(x, y, mousekey): Promise<void>

发送点击信号,缺省为左键点击目标控件中心位置。

  • x: number类型,相对坐标的水平像素,缺省为水平中心;
  • y: number类型,相对坐标的垂直像素,缺省为垂直中心;
  • mouseKey: 鼠标左右键,1为左键,2为右键;缺省值为1;
  • 返回值: 不返回任何值的异步方法。

最简单的调用方式就是不使用任何参数调用,效果是鼠标左键点击目标控件的正中心。

这里假设目标控件为按钮控件Button

JavaScript
Python
await model.getButton("Button").click()
model.getButton("Button").click()

如果需要点击控件的指定坐标位置,可以传入相对控件左上角的水平像素值(x)和垂直像素值(y

JavaScript
Python
await model.getButton("Button").click(590, 10)
model.getButton("Button").click(590, 10)

需要注意的是,click(0, 0)并不会点击控件左上角原点,而是点击控件正中心;如果期望点击左上角,可以用click(1, 1)代替。

如果需要右击控件,则需要传入第三个参数并设置为2:

JavaScript
Python
await model.getButton("Button").click(590, 10, 2)
model.getButton("Button").click(590, 10, 2)
如果需要右键点击控件正中心,也可以使用下面这种语法:

JavaScript
Python
await model.getButton("Button").click(0, 0, 2) 
await model.getButton("Button").click(null, null, 2)
model.getButton("Button").click(0, 0, 2)
model.getButton("Button").click(None, None, 2)

dblClick(x, y, mousekey): Promise<void>

发送双击信号,缺省为左键双击目标控件中心位置。

  • x: number类型,相对坐标的水平像素;缺省为水平中心;
  • y: number类型,相对坐标的垂直像素;缺省为垂直中心;
  • mouseKey: number类型,鼠标左右键,1为左键,2为右键;缺省值为1;
  • 返回值: 不返回任何值的异步方法。

使用方法可参考上面的click()

moveMouse(x, y, seconds): Promise<void>

将鼠标位置移至指定位置,xy表示相对于控件位置的水平和垂直像素坐标。第三个参数用于控制鼠标移动的速度,数值越大移动越慢,数值越小移动越快。

  • x: number类型,水平像素坐标;
  • y: number类型,垂直像素坐标;
  • seconds: number类型,鼠标移动的持续时间,默认值为1,即1秒;
  • 返回值: 不返回任何值的异步方法。

使用示例

如果不传入任何参数,则鼠标会默认移动到控件的中心位置。以下示例假设目标控件为名为Pane的按钮控件。

JavaScript
Python
await model.getPane("Pane").moveMouse()
model.getPane("Pane").moveMouse()
如果需要移动鼠标到控件的指定坐标位置,可以传入相对控件左上角的水平像素值(x)和垂直像素值(y

JavaScript
Python
await model.getPane("Pane").moveMouse(500, 70)
model.getPane("Pane").moveMouse(500, 70)

请注意,moveMouse(0, 0)并不会将鼠标移动到控件左上角原点,而是将鼠标移动到控件的正中心位置。如果想要移动到左上角,可以使用moveMouse(1, 1)

如果需要控制鼠标移动的速度,可以传入第三个参数来指定持续时间,单位为秒。

JavaScript
Python
await model.getPane("Pane").moveMouse(500, 70, 5)
model.getPane("Pane").moveMouse(500, 70, 5)

wheel(value): Promise<void>

发送鼠标滚轮信号。

  • value: number类型,正值滚轮往前滚动,负值往后滚动。例如 1往前滚动一格,-1往后滚动一格
  • 返回值: 不返回任何值的异步方法。

drag(x, y, mouseKey): Promise<void>

拖拽操作的第一步操作,在目标位置按住鼠标不松开,xy为相对控件位置的水平像素和垂直像素,缺省为中心位置。

  • x: (可选)number类型,相对于控件水平中心的偏移像素,默认值为控件水平中心。
  • y: (可选)number类型,相对于控件垂直中心的偏移像素,默认值为控件垂直中心。
  • mouseKey:(可选)number类型,拖拽时使用的鼠标键,1表示左键,2表示右键。默认值为左键(1)。
  • 返回值: 不返回任何值的异步方法。

可以参考拖拽控件的方式选择

drop(x, y, options): Promise<void>

拖拽操作的第二步操作,将鼠标位置移至指定位置并松开鼠标,xy为相对控件位置的水平像素和垂直像素。第三个参数用于控制鼠标移动的速度和使用的鼠标键。

  • x: (可选)number类型,相对于控件水平中心的偏移像素,左为负值,右为正值。默认值为0
  • y: (可选)number类型, 相对于控件垂直中心的偏移像素,上为负值,下为正值。默认值为0
  • options:(可选)object类型,一个配置对象,用于自定义拖拽行为。支持的选项包括:
    • duration: number类型,鼠标移动到目标位置的持续时间,单位为秒。默认值为1,即1秒;
    • mouseKeys: number类型,在拖拽操作中使用的鼠标按键。1表示左键,2表示右键。默认值为1
  • 返回值: 不返回任何值的异步方法。

可以参考拖拽控件的方式选择

pressKeys(keys, options?): Promise<void>

输入按键或字符串,输入前会聚焦目标控件。传入字符串时,字符串中的一些特殊字符(^+~%{}())会被当作控制键(Shift键、CTRL键等)来执行,而不会输入相应的符号,具体可以参考附录: 输入键对应表。如果希望输入纯文本,无视这些控制键符号,则可以使用{textOnly: true}选项,这样调用:pressKeys(str, {textOnly: true})

  • keys: string类型,要输入的按键、组合键或是字符串,最大支持1024个字符。
  • options: (可选)控制输入模式的一些可选参数。
    • textOnly: 仅输入字符串,并将控制字符也当作文本进行输入。效果等同于调用Keyboard.typeString()
    • cpm: 即每分钟输入字符数,用于控制文本输入速度。自动化操作时建议cpm值设置在200以上。由于方法的内部实现,以及各个系统、应用对文本输入的处理不相同,实际输入速度不一定能达到设置的cpm。 当options为数字时相当于cpm参数。
  • 返回值: 不返回任何值的异步方法。 更多相关说明或样例,请参照模拟按键输入pressKeys方法

exists(seconds: number): Promise<boolean>

检查控件是否存在,输入参数为检查等待的最长时间(超时时间),当控件出现时立刻返回true,而当控件匹配结果出错或等待到超时返回false。该方法默认只会检查一次,不会重试或等待。

  • seconds: (可选)number类型,表示最长等待时间(超时时间),单位为秒。默认为0,即只检查一次,立即返回结果,不会等待或重试。
  • 返回值: Promise<boolean>类型,异步的返回控件的存在结果。

使用示例

不传入参数时,默认只检查一次。

JavaScript
Python
await model.getApplication("dirview").exists();
model.getApplication("dirview").exists()

传入最长等待时间,如果在指定的最长等待时间内检测到控件存在,则立即返回true。 如果控件匹配结果出错或超过指定的最长等待时间,则返回false

JavaScript
Python
let bool = await model.getApplication("dirview").exists(10);
assert.strictEqual(bool, true);
bool = model.getApplication("dirview").exists(10)
assert bool == True

highlight(duration: number): Promise<void>

控件高亮,可以传入参数指定高亮持续毫秒时间,默认值为1000,即持续1秒。

  • duration: (可选)number类型,高亮持续时间,单位为毫秒。
  • 返回值: 不返回任何值的异步方法。

takeScreenshot(filePath?: string): Promise<void | string>

控件截图,可以传入路径来将截图保存到路径位置。

  • filePath: (可选)string类型,截图的保存路径和文件名,如"./images/screenshot1.png"
  • 返回值: 异步的返回截图的base64字符串。

使用示例

takeScreenshot()方法返回的base64编码图片,可以直接作为图片插入到运行报告中,详细请参考报告附件

invoke(methodName: string, args?: (string | number | boolean)[]): Promise<object>

调用控件的指定方法,并返回方法的执行结果。

  • methodName: string类型,要执行的方法的名称。
  • args(可选): 字符串、数字或布尔值的数组,传递给方法的参数列表。
  • 返回值: 异步的返回方法调用后的执行结果。

使用示例

JavaScript
Python
// 获取表格行数
const result = await table.invoke("model.rowCount", []);
// 获取表格第1行第1列单元格的值
const cellValue = await table.invoke("model.data", [0, 0]);
# 获取表格行数
result = table.invoke("model.rowCount", [])
# 获取表格第1行第1列单元格的值
cellValue = table.invoke("model.data", [0, 0])
根据不同的控件和方法,invoke方法可以接受不同的参数。例如,在table控件中,可以使用invoke方法获取表格的行数和列数,甚至是直接获取表格内部的所有数据,如上述示例所示。根据具体的需求,可以传递不同的参数来调用不同的方法。

请注意,根据具体的应用场景和控件,invoke方法的参数和返回值可能会有所不同。请参考文档、源代码或API文档中对该类的方法的描述,以获取准确的参数和返回值信息。

属性方法

rect(): Promise<Rect>

获取目标控件的形状与位置信息。

  • 返回值: 返回一个Rect对象,包含控件的坐标信息xy,宽度和高度信息widthheight

winId(): Promise<number>

获取目标控件的窗口句柄ID。

  • 返回值: 异步的返回一串代表窗口句柄ID的数字。

enabled(): Promise<boolean>

获取目标控件的可用情况,当控件不可用时,不会响应点击或输入操作。

  • 返回值: 异步的返回目标控件是否可用的布尔值。

visible(): Promise<boolean>

获取目标控件的可见情况,当控件不可见时,不会在桌面中显示,也无法进行操作。

  • 返回值: 异步的返回目标控件是否可见的布尔值。

modelProperties(): Promise<object>

获取目标控件的模型属性,也即对应模型对象的属性(包括识别属性和其它属性)。

  • 返回值: 异步的返回目标模型对象的属性组成的对象object

使用示例

JavaScript
Python
let modelProperties = await model.getWindow("Standard_Dialogs").modelProperties()
console.log(modelProperties)
assert.equal('Window',modelProperties.type)
modelProperties = model.getWindow("Standard_Dialogs").modelProperties()
print(modelProperties)
assert modelProperties['type'] == 'Window'
输出的modelProperties为

{ type: 'Window', className: 'Dialog', text: 'Standard Dialogs' }

通过调用modelProperties()方法,可以获取目标控件的模型属性,包括识别属性和其他属性。返回的modelProperties对象包含了目标模型对象的各种属性信息,例如类型(type)、类名(className)和文本(text)等。

在示例中,我们首先使用model.getWindow("Standard_Dialogs")获取目标窗口控件的模型对象,然后调用modelProperties()方法来获取模型属性。最后,我们将modelProperties对象打印输出,并使用断言来验证type属性的值是否为Window

这样,我们可以方便地获取目标控件的模型属性,并进行进一步的验证和处理。

modelImage(options?: {encoding: 'buffer' | 'base64'}): Promise<Buffer | string>

获取目标控件在模型中的截屏文件内容。

  • options:
    • encoding: bufferbase64两个选项,返回值的格式。
  • 返回值: 异步的返回目标控件在模型中的截屏文件流(base64编码或是Buffer)。

allProperties(): Promise<object>

获取目标控件的所有运行时属性,以对象的形式返回。

  • 返回值: 异步的返回目标模型对象的属性组成的对象object。 例如:

JavaScript
Python
let properties = await model.getButton("Button").allProperties();
console.log(properties);
console.log('objectName=' + properties['objectName']); //只获取其中的一个属性,如objectName
properties = await model.getButton("Button").allProperties()
print(properties)
print('objectName=' + properties['objectName']); #只获取其中的一个属性,如objectName

会获取按钮的所有属性,并打印:

{
  "acceptDrops": false,
  "accessibleDescription": "",
  "accessibleName": "",
  "arrowType": 0,
  "autoExclusive": false,
  ...
  "objectName": "按钮1",
  "x": 11,
  "y": 11
}

上面的代码第3行会打印某一个属性:

objectName=按钮1

property(name): Promise<any>

获取目标控件的指定运行时属性。支持传入各种控件的属性名,同时也支持直接使用allProperties()方法中的字段名,详见下面的代码示例。

如果传入了一个不存在的属性名,则会返回undefined,而不会出错。

  • name:string类型,属性名,详见可选属性字段
  • 返回值: 异步的返回各种属性值。 例如上面只获取一个属性的脚本:

JavaScript
Python
let properties = await model.getButton("Button").allProperties();
console.log(properties['objectName']); //只获取其中的一个属性,如objectName
properties = model.getButton("Button").allProperties()
print(properties['objectName']); #只获取其中的一个属性,如objectName

可以直接调用property()获得:

JavaScript
Python
let objectName = await model.getButton("Button").property('objectName');
objectName = model.getButton("Button").property('objectName')

waitProperty(propertyName: QtPropertyIds, value: string | number | boolean, timeOutSeconds: number): Promise<boolean>

等待属性出现某值。

  • propertyName: 属性名,支持的属性名有enabled, text, value等,详见可选属性字段
  • value: 属性值。
  • timeOutSeconds: 等待超时秒设置,缺省为5秒,-1将无限等待。
  • 返回值: 表示是否成功等到该值出现。如果报错则代表没有匹配到该控件,有可能是该控件未出现,或者是对象名称有问题。

下面的样例代码等待按钮由禁用状态变为可点击,然后点击它:

JavaScript
Python
await model.getButton("下一步").waitProperty("enabled", true);
await model.getButton("下一步").click();
model.getButton("下一步").waitProperty("enabled", true)
model.getButton("下一步").click()
如果此控件有可能一直被禁用,可以通过判断它的返回值再执行点击或其他操作。

checkProperty(propertyName, expectedValue, optionsOrMessage): Promise<void>

校验控件的属性值是否符合预期。如果指定的属性值不匹配期望值,则会抛出错误。

你可以在录制过程中快捷生成基于此方法的属性检查脚本,详情请参考录制中添加属性检查点

参数:

  • propertyName: string类型,需要检查的属性名称。支持的属性名包括 enabledtextvalue 等,以及所有由 allProperties() 方法返回的字段。
  • expectedValue: string | number | boolean | RegExp类型,期望的属性值。当控件的属性值与期望值匹配时,测试将通过;否则会抛出错误。
  • optionsOrMessage: string | object类型,可选参数。如果是字符串,它将被用作自定义错误信息;如果是对象,则可包含以下字段:
    • message:string 类型,自定义的错误信息。
    • operation:any 类型,指定用于比较控件属性与期望值的运算符。默认值为 equal,即验证属性值是否等于期望值。

返回值:

  • 不返回任何值的异步方法。

使用示例

有关在 checkProperty() 中使用正则表达式的示例,请参考通过正则表达式检查

验证文本框控件的 text 属性是否为 "Hello World":

JavaScript
Python
await model.getEdit("textEdit").checkProperty('text', 'Hello World', 'Text does not match expected value');
model.getEdit("textEdit").checkProperty('text', 'Hello World', 'Text does not match expected value')

如果文本不符合预期,将抛出一个错误,错误信息为 "Text does not match expected value"。

checkImage(options?: CompareOptions): Promise<void>

用于验证控件的当前图像与预先定义或录制的参考图像是否匹配。如果存在差异,测试会抛出错误并显示图像间的差异。这个方法可用于自动化测试中的视觉校验,确保 UI 控件的视觉展现符合预期。

  • options (可选): 用于配置图像比较的选项。这些选项与 Image.imageEqual 方法的参数相同,包含以下属性:
    • colorTolerance: number 类型,定义了在颜色匹配时允许的最大差异容忍度。默认值为 0.1。
    • pixelPercentTolerance: number 类型,定义了允许的最大像素百分比差异容忍度。默认值为 1,表示最多允许 1% 的像素差异。
    • pixelNumberTolerance: number 类型,定义了允许的像素数量差异容忍度。如果设置为0,则无差异容忍。
    • ignoreExtraPart: boolean 类型,用于指定是否忽略图像中的额外部分。默认为 false。
    • auto:boolean类型,如果设置为true,则根据图像特征自动识别两幅图片,并将它们缩放到一致的比例进行比较,默认适应到较小尺寸那张图片的比例。
  • 返回值:不返回任何值的异步方法。

使用示例

假设我们有一个控件 Button,我们可以使用 checkImage() 来验证其当前显示的图像是否与预期相符。例如:

JavaScript
Python
await Button.checkImage({
    colorTolerance: 0.05,
    pixelPercentTolerance: 0.5
});
Button.checkImage({
    "colorTolerance": 0.05,
    "pixelPercentTolerance": 0.5
})

在这个例子中,checkImage 方法将会验证 Button 控件的图像与预期的参考图像在颜色容忍度为 0.05 和像素百分比差异容忍度为 0.5% 的条件下是否相匹配。如果图像存在超出这些指定容忍度的差异,测试将会抛出错误。

CukeTest提供了一系列的控件导航方法,使得用户可以使用导航方法检索到目标控件的父控件、子控件以及兄弟控件。通过导航方法可以借助一个锚点控件索引到整棵控件树中所有的控件,常用于输入框定位、控件遍历等场景。

导航方法都是异步方法,也就是需要一定时间在应用里面检索到被导航的控件并返回,并不是直接在模型管理器的树中进行导航,这点需要区分不要混淆。

有关控件导航的使用,可以参考附录H:CukeTest自带样例中的"文件浏览器遍历"。

firstChild(controlType?: QtControlType): Promise<IQtControl>

获取控件在应用中的第一个子控件,可以通过传入参数controlType指定子控件类型,可以实现筛选的效果,即获取应用中的第一个controlType类型的子控件,controlType为字符串,可选值可以参考QtControlType。如果没有任何满足条件的子控件则返回null。 同时,child仅代表直系的子控件,不会检索到孙控件,如果没有在子控件中检索到满足条件的控件,会直接返回null而不是继续在孙控件中搜索。如果仍希望获得某个控件下的所有控件,可以使用findControls方法。

  • controlType: (可选)字符串类型,可选值可以参考QtControlType
  • 返回值: 异步的任意控件类型,具体类型取决于导航到的控件类型。没有导航到任何控件时返回null

注意,如果某些控件是不可见或还未加载的,那么是不会被计入的,准确来说,firstChild()方法只是拿到可以拿到的子对象中的第一个,下面的lastChild()方法同样。

lastChild(controlType?: QtControlType): Promise<IQtControl>

获取控件在应用中的最后一个控件,可以通过传入参数controlType指定子控件类型,可以实现筛选的效果,即获取应用中的最后一个controlType类型的子控件,controlType为字符串,可选值可以参考QtControlType。如果没有任何满足条件的子控件则返回null

  • controlType: (可选)字符串类型,可选值可以参考QtControlType
  • 返回值: 异步的任意控件类型,具体类型取决于导航到的控件类型。没有导航到任何控件时返回null

next(controlType?: QtControlType): Promise<IQtControl>

获取控件的在应用的下一个兄弟控件,通常为相邻的右侧/下方节点,取决于容器的布局方向。可以通过传入参数controlType指定兄弟控件类型,可以实现筛选的效果,即获取应用中的下一个controlType类型的兄弟控件,controlType为字符串,可选值可以参考QtControlType。如果没有任何满足条件的兄弟控件则返回null,比如当控件作为父控件的lastChild时会返回null

  • controlType: (可选)字符串类型,可选值可以参考QtControlType
  • 返回值: 异步的任意控件类型,具体类型取决于导航到的控件类型。没有导航到任何控件时返回null

previous(controlType?: QtControlType): Promise<IQtControl>

next()方法相反,获取控件的在应用的上一个兄弟控件,通常为相邻的左侧/上方节点,取决于容器的布局方向。可以通过传入参数controlType指定兄弟控件类型,可以实现筛选的效果,即获取应用中的下一个controlType类型的兄弟控件,controlType为字符串,可选值可以参考QtControlType。如果没有任何满足条件的兄弟控件则返回null,比如当控件作为父控件的firstChild时会返回null

  • controlType: (可选)字符串类型,可选值可以参考QtControlType
  • 返回值: 异步的任意控件类型,具体类型取决于导航到的控件类型。没有导航到任何控件时返回null

parent(): Promise<IQtControl>

firstChild()lastChild()方法相反,获取控件的父控件,如果控件为最顶层控件则返回null

  • 返回值: 异步的任意控件类型,具体类型取决于导航到的控件类型。没有导航到任何控件时返回null

findControls(...conditions: ConditionFilter[]): Promise<IQtControl[]>

搜索方法可以让用户更精细、自主的在被测应用中使用用户给定的搜索条件匹配控件。可以理解为类似children的导航方法,会匹配目标控件中的所有子控件,并根据筛选条件进行筛选。因此可以直接传递一个筛选条件对象Criteria;另外还可以选择传入一个模型对象名称objectName,这会自动的使用该模型对象的识别属性作为筛选条件。

它以数组形式返回所有满足条件的控件对象。它能够根据传入的条件,实时获得应用上所有匹配的控件,并返回对象数组的Promise。该API可以用于有界面多个相似控件的时候,同时返回这一组对象,以便于对所有这些对应控件进行操作。

使用findControls()方法能够完成一些特殊情况下的任务:

场景一: 批量操作

借助findControls()方法能够返回多个控件对象的特性,对于应用中的大量相似控件进行遍历操作。比如要选中名为“Dock”的容器控件中所有的复选框,可以通过如下脚本完成:

JavaScript
Python
let checkboxes = await model.getGeneric("Dock").findControls({type:"CheckBox"});
// 此时checkboxes变量即为一个全为复选框对象的数组
for(let index in checkboxes){
    await checkboxes[index].check();
}
checkboxes = model.getGeneric("Dock").findControls({type:"CheckBox"})
# 此时checkboxes变量即为一个全为复选框对象的数组
for index in checkboxes:
    checkboxes[index].check()

all(): Promise<IQtControl[]>

all()方法返回与当前控件的“标识属性”相匹配的所有同级控件数组,它简化了在测试中对同一层级多个控件的批量操作,尤其适用于处理表格、列表或一组按钮等场景。

  • 返回值: 异步的任意控件类型数组,具体类型取决于导航到的控件类型。没有导航到任何控件时返回null

findControls()相比,all()更适用于无需特定筛选条件就要操作或检查当前控件同级所有控件的情况。

使用示例

假设在一个应用界面中有多个按钮排列在一起,您可能需要验证这些按钮的可用性或者依次进行点击。使用all()方法,可以轻松获取这一层级的所有按钮控件,并进行遍历操作。

JavaScript
(async function() {
    let controls = await model.getButton("Normal").all()
    console.log('控件数量', controls.length); // 输出匹配到的控件数量
    for(let control of controls) {
        console.log(await control.name()); // 打印每个控件的名称
        // 执行其他操作...
    }
})()

在这个示例中,model.getButton("Normal").all()调用会返回当前模型下所有按钮控件的数组。然后,通过遍历这个数组,可以实现对每个按钮的文本打印和点击操作。这种方法简化了对同级多个控件的操作流程,提高了测试脚本的效率和可读性。

可能用到的类

可选属性字段PropertyName

下面是Qt自动化支持的属性名,可以用于property()waitProperty()以及checkProperty()方法中。

控件类型 属性字段
QtControl winId
QtControl enabled
QtControl rect
QtControl visible
QTab count
QTab activeIndex
QTab activeName
QTab tabNames
QTabItem text
QTabItem itemIndex
QButton text
QLabel text
QCalendar date
QTabBar count
QTabBar activeIndex
QTabBar activeName
QTabBar tabNames
QItemView data
QItemView selectedIndex
QItemView rowCount
QItemView columnCount
QMenuBarItem itemIndex
QMenuBarItem text
QMenu itemNames
QTable columnHeaders
QTable rowHeaders
QTable data
QTable rowCount
QTable columnCount
QTableItem value
QTableItem rowIndex
QTableItem columnIndex
QTree columnHeaders
QTree childCount
QTree columnCount
QTreeItem value
QTreeItem rowData
QTreeItem expandable
QTreeItem expanded
QTreeItem childCount
QTreeItem rowIndex
QTreeItem columnIndex
QTreeItem itemPath
QList data
QList selectedIndex
QList itemCount
QListItem value
QListItem itemIndex
QRadioButton checked
QCheckBox checkState
QComboBox selectedIndex
QComboBox selectedName
QDial value
QEdit value

除了以上列出来的属性外,各个控件还各自维护了一些特有的内部属性名,可以参考allProperties()方法的返回结果对象中的字段。

Rect类型

用于描述控件形状与位置信息的对象。在CukeTest中,所有控件都可以用一个矩形(Bounding Rectangle)来描述位置和形状信息,Rect对象包含以下属性:

  • x: number类型,相对坐标的水平像素;
  • y: number类型,相对坐标的垂直像素;
  • width: number类型,水平宽度,单位为像素;
  • height: number类型,垂直高度,单位为像素;

本篇只介绍了对象操作的基本概念和两个基类的方法,更多关于API介绍的文档可以继续阅读下一篇:基础类型对象的API

另外,具体每个操作和属性的定义都可以在模型管理器中找到,也可以打开CukeTest中自带的Qt自动化API库的类型文件——leanpro.qt的类型文件进行查找,具体方法可以查看更多API介绍一节的介绍。

Qt控件类型枚举类: QtControlType

包含了所有Qt控件类型,通常用于findControls()方法或是导航方法获取指定类型的控件对象。

JavaScript
enum QtControlType {
    Button = 'Button',
    Calendar = 'Calendar',
    CheckBox = 'CheckBox',
    ComboBox = 'ComboBox',
    Dial = 'Dial',
    ItemView = 'ItemView',
    ViewItem = 'ViewItem',
    Label = 'Label',
    List = 'List',
    ListItem = 'ListItem',
    GraphicsView  = 'GraphicsView',
    GroupBox = 'GroupBox',
    Window = 'Window',
    Menu = 'Menu',
    MenuItem = 'MenuItem',
    MenuBar = 'MenuBar',
    MenuBarItem = 'MenuBarItem',
    Edit = 'Edit',
    Pane = 'Pane',
    ProgressBar = 'ProgressBar',
    RadioButton = 'RadioButton',
    ScrollArea = 'ScrollArea',
    Slider = 'Slider',
    SpinBox = 'SpinBox',
    StatusBar = 'StatusBar',
    Tab = 'Tab',
    TabBar = 'TabBar',
    TabItem = 'TabItem',
    Table = 'Table',
    TableItem = 'TableItem',
    ToolBar = 'ToolBar',
    ToolBox = 'ToolBox',
    Tree = 'Tree',
    TreeItem = 'TreeItem',
    QuickItem = 'QuickItem',
    Custom = 'Custom',
    Application = 'Application',
}

控件筛选条件类: Criteria

JavaScript
Python
export interface Criteria {
    appName?: string,
    type?: QtControlType | string,
    className?: string,
    name?: string,
    index?: number,
    itemPath?: ItemPath,
    itemIndex?: number,
    text?: string,
    toolTip?: string,
    'text~'?: string,
    'name~'?: string,
    'toolTip~'?: string
}
class Criteria():
	appName: str
	type: str
	className: str
	name: str
	index: int
	itemPath: TypedDict
	itemIndex: int
	text: str
	toolTip: str

控件路径

用于描述列表项控件ListItem、树视图项控件TreeItem和表格单元格控件TableItem所处位置的对象。

JavaScript
export type ItemPath = (number | [number, number])[];

API调用技巧

同步方法与异步方法的区分技巧

IQContainer作为容器类,提供了获取对象的API,如下:

JavaScript
Python
export interface IQtContainer {
    parent: IQtContainer;
    getGeneric(...conditions: ConditionFilter[]): IQtControl;
    getWindow(...conditions: ConditionFilter[]): IQWindow;
    getButton(...conditions: ConditionFilter[]): IQButton;
    ......
    getGraphicsItem(...conditions: ConditionFilter[]): IQGraphicsItem
    getQuick(...conditions: ConditionFilter[]): IQuickItem
    ......
    getTable(...conditions: ConditionFilter[]): IQTable;
    getTableItem(...conditions: ConditionFilter[]): IQTableItem;
    getTree(...conditions: ConditionFilter[]): IQTree;
    getTreeItem(...conditions: ConditionFilter[]): IQTreeItem;
    getVirtual(...conditions: ConditionFilter[]): IVirtual;
}
class QtContainer():
	def getApplication(*conditions: List[Union[str, Dict]]) -> "QApplication"
	def getGeneric(*conditions: List[Union[str, Dict]]) -> "QtControl"
	def getWindow(*conditions: List[Union[str, Dict]]) -> "QWindow"
	def getButton(*conditions: List[Union[str, Dict]]) -> "QButton"
	def getCalendar(*conditions: List[Union[str, Dict]]) -> "QtControl"
	def getCheckBox(*conditions: List[Union[str, Dict]]) -> "QCheckBox"
	def getComboBox(*conditions: List[Union[str, Dict]]) -> "QComboBox"
	def getEdit(*conditions: List[Union[str, Dict]]) -> "QEdit"
	def getGraphicsItem(*conditions: List[Union[str, Dict]]) -> "QGraphicsItem"
  def getQuick(*conditions: List[Union[str, Dict]])-> "QuickItem"
	def getItemView(*conditions: List[Union[str, Dict]]) -> "QItemView"
	def getViewItem(*conditions: List[Union[str, Dict]]) -> "QItemViewItem"
	def getList(*conditions: List[Union[str, Dict]]) -> "QList"
	def getListItem(*conditions: List[Union[str, Dict]]) -> "QListItem"
	def getMenu(*conditions: List[Union[str, Dict]]) -> "QMenu"
	def getMenuBar(*conditions: List[Union[str, Dict]]) -> "QMenuBar"
	def getMenuBarItem(*conditions: List[Union[str, Dict]]) -> "QMenuBarItem"
	def getMenuItem(*conditions: List[Union[str, Dict]]) -> "QMenuItem"
	def getRadioButton(*conditions: List[Union[str, Dict]]) -> "QRadioButton"
	def getSpinBox(*conditions: List[Union[str, Dict]]) -> "QSpinBox"
	def getTab(*conditions: List[Union[str, Dict]]) -> "QTab"
	def getTabBar(*conditions: List[Union[str, Dict]]) -> "QTabBar"
	def getTabItem(*conditions: List[Union[str, Dict]]) -> "QTabItem"
	def getTable(*conditions: List[Union[str, Dict]]) -> "QTable"
	def getTableItem(*conditions: List[Union[str, Dict]]) -> "QTableItem"
	def getTree(*conditions: List[Union[str, Dict]]) -> "QTree"
	def getTreeItem(*conditions: List[Union[str, Dict]]) -> "QTreeItem"
	def getVirtual(*conditions: List[Union[str, Dict]]) -> "Virtual"
	def getPattern(*conditions: List[Union[str, Dict]]) -> "Pattern"

对于所有常用的控件"Widget",会有容器方法"get[Widget]"返回Widget对应的对象。例如Button控件,有提供getButton返回按钮的对象。

如果和上节中的对象操作API的返回值比较可以立刻注意到,获取对象API的返回值都是直接的数据类型,而不是Promise<T>,这代表这些都是同步方法,即不需要添加await关键字即可取到返回结果。而对象操作API中也有一个特别的同步方法,就是属于TableListTree控件的getItem方法,它也是对象操作API中唯一一个同步方法。

CukeTest中同步方法和异步方法从命名上就有区别,含get前缀的方法是同步方法,无需await关键字;而没有这个前缀的是异步方法,不加await的话只会返回一个Promise对象,加上await才是正确的返回值。

使用模糊匹配(正则匹配)获取控件

观察获取对象使用的Criteria对象可以发现里面有几个特殊属性,它们以~作为后缀,同时在Criteria对象中有同名的属性。
这些带~后缀的属性代表这些属性允许使用模糊匹配,或者也叫作正则匹配。可以在模型管理器中修改匹配模式。也可以在脚本中使用如下方式进行匹配:

JavaScript
Python
// 全文匹配
await model.getButton({"name": "数字5"}).click();
// 匹配所有name属性包含“数字”的控件
await model.getButton({"name~": "数字"}).click();
// 匹配所有name属性以“5”结尾的控件
await model.getButton({"name~": "数字$"}).click();
# 全文匹配
model.getButton({"name": "数字5"}).click()
# 匹配所有name属性包含“数字”的控件
model.getButton({"name~": "数字"}).click()
# 匹配所有name属性以“5”结尾的控件
model.getButton({"name~": "数字$"}).click()

这些属性都可以在模型管理器里面通过“[复制节点属性]”功能直接生成,不一定需要自己手动构造。

更多API介绍

更多的控件的操作和属性的帮助可以在模型管理器中找到,或者在拖拽模型文件生成的脚本中,按住Ctrl键点击脚本头部的leanpro.qt引用跳转到类型定义文件。

results matching ""

    No results matching ""