Get QML element in Squish by id or objectName using findObject() or waitForObject() without object map

StackOverflow https://stackoverflow.com/questions/22091457

  •  18-10-2022
  •  | 
  •  

Frage

How to get QML element by id or objectName using either findObject() or waitForObject() without object map? Is it even possible?

Consider:

Item {
    id: page
    objectName: "pageObject"

    Text {
        id: foobar
        objectName: "lorem"
        text: "ipsum"
    }
}

I would like to access foobar's text in test script like:

obj = findObject("foobar")
if obj.text == "ipsum":
    test.passes("all good")
else:
    test.fail("uh oh")

I have also tried:

obj = findObject("lorem")
obj = findObject("{name='lorem'}")
obj = findObject("{name='lorem' type='Text'}")
obj = findObject("{objectName='lorem'}")
obj = findObject("{objectName='lorem' type='Text'}")
War es hilfreich?

Lösung

Ultimately I solved the issue by only having the root level object in object map. All other objects are iterated in code using functions below.

import sys
import time

# Definitions
ROOT_SYMBOLIC_OBJECT = ":_KMainView"

# Object finding functions
def findSymbolic(symbolicName):
    # Note: give Squish symbolic name like ":Foobar"
    try:
        obj = findObject(symbolicName)
    except LookupError:
        sys.exc_clear()
        test.log("FAIL: Root object not found: " + symbolicName)
        sys.exit()

    return obj

def findRoot():
    return findSymbolic(ROOT_SYMBOLIC_OBJECT)

def findFlat(fromElement, findId, occurence, stopOnFail, verbose):
    start = time.time()
    result = None
    found = 0

    if fromElement and hasattr(fromElement, 'children'):
        children = object.children(fromElement)
        for child in children:
            if hasattr(child, 'id') and child.id == findId:
                found += 1
                if found == occurence:
                    result = child
                    break

    if result == None and stopOnFail:
        test.log("FAIL: findFlat: " + findId + " not found")
        sys.exit()

    if verbose:
        printElapsed("findFlat", findId, start)

    return result

def findRecursive(fromElement, findId, occurence):
    return findRecursiveWithOptions(fromElement, findId, occurence, True, False, True)

def findRecursiveWithOptions(fromElement, findId, occurence, stopOnFail, verbose, skipUnnamed):
    start = time.time()
    found = 0
    depth = -1

    obj, found, depth = objIter(fromElement, findId, occurence, verbose, skipUnnamed, found, depth)

    if found == occurence:
        printElapsed("findRecursive ok", findId, start)
        return obj

    printElapsed("findRecursive not found", findId, start)
    if stopOnFail:
        test.log("FAIL: findRecursive:" + findId + " not found.")
        sys.exit()

    return None 

def objIter(fromElement, findId, occurence, verbose, skipUnnamed, found, depth):
    depth += 1

    if verbose:
        printObjIter(fromElement, depth)

    children = object.children(fromElement)
    for child in children:
        if hasattr(child, 'id'):
            if child.id == findId:
                found += 1
                if found == occurence:
                    return child, found, depth
        elif skipUnnamed:
            continue

        obj, found, depth = objIter(child, findId, occurence, verbose, skipUnnamed, found, depth)
        depth = depth - 1
        if found == occurence: 
            return obj, found, depth

    return None, found, depth

def findRecursiveList(fromElement, findId):
    return findRecursiveListWithOptions(fromElement, findId, True, False, True)

def findRecursiveListWithOptions(fromElement, findId, stopOnFail, verbose, skipUnnamed):
    start = time.time()
    objList = []
    depth = -1

    objList, depth = objListIter(fromElement, findId, verbose, skipUnnamed, objList, depth)

    printElapsed("findRecursiveList", findId, start)
    return objList 

def objListIter(fromElement, findId, verbose, skipUnnamed, objList, depth):
    depth += 1

    if verbose:
        printObjIter(fromElement, depth)

    children = object.children(fromElement)
    for child in children:
        if hasattr(child, 'id'):
            if child.id == findId:
                objList.append(child)
        elif skipUnnamed:
            continue

        objList, depth = objListIter(child, findId, verbose, skipUnnamed, objList, depth)
        depth = depth - 1

    return objList, depth

# Utilities
def printElapsed(functionName, objectId, start):
    elapsed = time.time() - start
    test.log(functionName + " - " + objectId + " in " + str(elapsed) + "s.")

def printObjIter(element, depth):
    dashes = "-" * depth

    if hasattr(element, 'id'):
        test.log(dashes + " " + str(element.id))
    else:
        test.log(dashes + " [unnamed]")

Example test script for the question's QML:

startApplication("exampleapp") # Launch application binary 'exampleapp'
snooze(10) # Let it start

root = findRoot()
page = findFlat(root, "page", 1, True, False)
foobar = findFlat(page, "foobar", 1, True, False)

# Also just: foobar = findRecursive(root, "foobar", 1)

if foobar.text == "ipsum":
    test.passes("text is ok")
else:
    test.fail("Incorrect foobar text: " + foobar.text)

Andere Tipps

You can replace the symbolic name ":_KMainView" stored in ROOT_SYMBOLIC_OBJECT with its real name just as well, because symbolic name are just place holders for real names:

Article - How Squish looks up Real Names from Symbolic Names

The syntax should be: findObject("{container=':_QQuickView' id='foobar' type='Text' visible='true'}")

You may find the information in [Navigate] -> [Open Symbolic Name], and then choose the element and right-click on mouse to "Copy Real Name"

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top