Ask Your Question
0

UNO: find hierarchy till a selected object

asked 2019-07-19 04:40:38 +0200

Hi-Angel gravatar image

Main problem I'm having with UNO API so far is being able to find path to a particular document object in terms of objects hierarchy. For example: to access the first cell in LO Calc, the path one have to use is (in python):

CurrentComponent.CurrentController.ActiveSheet.Rows.getByIndex(0).getCellByPosition(0, 0)

But to find this hierarchy I had to introspect a lot of different objects, searching for the magic field that might get me one step closer. This is lots of time and effort!

Offhand, I imagine, it's possible to write in Python a code that iterates over everything in search for a value you passed it (such as a text), and prints the path upon finding.

Overall this, seems, should be very frequent problem, so sure someone already wrote a function or a code to do something like that. Any hints?

edit retag flag offensive close merge delete

Comments

The same cell object could be also accessed e.g. using

CurrentComponent.Sheets.getByIndex(0).getCellByPosition(0, 0)

... (given you have first sheet selected) and many others: e.g.,

CurrentComponent.CurrentController.ActiveSheet.getCellByPosition(0, 0)
CurrentComponent.Sheets.getByIndex(0).Columns.getByIndex(0).getCellByPosition(0, 0)
CurrentComponent.Sheets.getByName("Sheet1").getCellRangeByName("A1:B2").getCellByPosition(0, 0)
CurrentComponent.CurrentController.ActiveSheet.getCellRangeByName("A1")

So - which one of the multiple "hierarchies" should be given? And how would any introspection tool be able to inspect infinite set of possible "paths" in the search of this object? Which is actually answering your question: there's no way to tell you generally. Of course, a tool could give you how it has obtained the selection. Which is likely simply

CurrentComponent.CurrentController.getSelection
Mike Kaganski gravatar imageMike Kaganski ( 2019-07-19 05:01:16 +0200 )edit

@MikeKaganski I'm fine with any way it could've obtained it that is not using getSelection(), because generally I can follow any of them, except of course the one with selection because most of the times you won't have the object you want to work on selected.

Hi-Angel gravatar imageHi-Angel ( 2019-07-19 08:28:09 +0200 )edit

I hope you noticed that while introspection tool uses the method you wouldn't like, the real "path" to the object is unknown to a tool. And there might be infinite number of such ways; taking into account that many properties and methods create loops (when two objects reference each other in one of their members), there's no way to give you what you want.

Mike Kaganski gravatar imageMike Kaganski ( 2019-07-19 08:31:08 +0200 )edit

FTR: in implementing at least a limited (e.g. by depth) version of a search I stumbled upon limitation in UNO API that may make it harder to implement: not every object is exposed with iterators. E.g. cells aren't, one have to call very specific function getCellByPosition() with specific arguments. It's of course possible to handle that case, but makes me wonder if there's other objects that I can't get simply by calling iter() on some of their parents.

Hi-Angel gravatar imageHi-Angel ( 2019-07-21 03:49:26 +0200 )edit

1 Answer

Sort by » oldest newest most voted
1

answered 2019-07-22 01:53:20 +0200

Hi-Angel gravatar image

updated 2019-07-22 02:10:03 +0200

I wrote some search function, though it's is far from being satisfactory, because:

  1. This bug doesn't yet allow to exclude "going in loops" for walking through the hierarchy.
  2. Some objects that ought to have iterators doesn't have them. Luckily, so far I only stumbled upon one such object: XCellRange, it allows to iterate with getCellByPosition(), but not with iter(myCellRange) in python. Currently, searchLimited() function simply does not descend into these.

Still, I was able to implement something limited, and some minutes ago from me writing this I managed to successfully apply searchLimited() for finding something I couldn't figure out from searching the internet (which is: "given existing PageFields PivotTable filter, how to modify rows it filters out"). That makes me think, the code is worth putting out at the current state. When you absolutely desperate at finding the hierarchy, it may well save your day ☺


Repo for the code is here. The current version is at the end of this post, and consists of 3 functions:

  • getmembers_uno: basically, modified version of vanilla getmembers() from inspect module. I had to modify it because accessing some properties in UNO results in exceptions.
  • isiter: tests whether given object is iterable
  • searchLimited a depth-first search function with various knobs to alter its behavior. And, oh, believe me, you will want to do it, because occasionally you may stumble upon UNO objects with so many properties that dir() function gonna hang for dozens of minutes, basically, not doing anything useful for you.

    For that reason btw I left a print('TRACE:…') function, so you can see whether it's progressing, or got stuck and where; then interrupt it, and use ignore() hook to ignore the field where it got stuck (or simply to ignore fields where, you're sure, not gonna be anything useful).

Here's modified snip from the session where it helped me:

>>> ignore = lambda s: 'Links' in s or 'Picture' in s or 'MasterPage' in s
>>> searchLimited(pilotTable, 'Publisher', 5, lambda p: isinstance(p, str), ignore)
…[lots of TRACE output snipped]
'FOUND: <TOPLEVEL>.DataPilotFields.<iter 9>.Items.<iter 3>.Name'

What this says is that I can find the Publisher name if I evaluate pilotTable.DataPilotFields.getByIndex(9).Items.getByIndex(3).Name. (and if anyone interested how to use that to filter PivotTable: altering ….getByIndex(3).IsHidden will do it)

Code:

import uno
from inspect import *

# this is a modified version of inspect.getmembers(). It was modified to not fail on
# uno exceptions that sometimes happen during access attempts
# And while on it: ignore uno.ByteSequence by default.
# returns: [(String, a)]
def getmembers_uno(object, predicate=lambda obj: not isinstance(obj, uno.ByteSequence)):
    """Return all members of an object as (name, value) pairs sorted by name.
    Optionally, only return members that satisfy a given predicate."""
    if isclass(object):
        mro = (object,) + getmro(object)
    else:
        mro = ()
    results = []
    processed = set()
    names = dir(object)
    # :dd any DynamicClassAttributes to the list of names if object is a class;
    # this may result ...
(more)
edit flag offensive delete link more

Comments

1

Nice! 👍🏻

Mike Kaganski gravatar imageMike Kaganski ( 2019-07-22 01:58:55 +0200 )edit
Login/Signup to Answer

Question Tools

1 follower

Stats

Asked: 2019-07-19 04:40:38 +0200

Seen: 33 times

Last updated: Jul 22