pywinauto: Iterate through all controls in a window - python

I'm trying to write a general test script to find errors in new software builds. My idea is to iterate through the controls in the window and interact with each one, logging any errors that are caused and restarting the software if it crashes.
I'm looking for a way to dynamically find control identifiers, a bit like print_control_identifiers() but with the output being a list or similar structure which I can iterate through.
On a GitHub question about control identifiers this was mentioned:
it's possible to walk the hierarchy by using .children() (immediate children only) and .descendants() (the whole subtree as a plain list)
I assumed I could just iterate through my Application object's descendants() list and call a relavant interaction method for each, however I can't work out how to get this list. I assumed I could do something like this, but I haven't had any success:
def test(application):
for child in application.descendants():
#interact with child control
software = Application(backend='uia').start(cmd_line=FILE_PATH)
test(software)
AttributeError: Neither GUI element (wrapper) nor wrapper method 'descendants' were found (typo?)
EDIT
I resorted to looking through the code and found the print_control_identifiers method:
class Application(object):
def print_control_identifiers(self, depth=None, filename=None):
"""
Prints the 'identifiers'
Prints identifiers for the control and for its descendants to
a depth of **depth** (the whole subtree if **None**).
.. note:: The identifiers printed by this method have been made
unique. So if you have 2 edit boxes, they won't both have "Edit"
listed in their identifiers. In fact the first one can be
referred to as "Edit", "Edit0", "Edit1" and the 2nd should be
referred to as "Edit2".
"""
if depth is None:
depth = sys.maxsize
# Wrap this control
this_ctrl = self.__resolve_control(self.criteria)[-1]
# Create a list of this control and all its descendants
all_ctrls = [this_ctrl, ] + this_ctrl.descendants()
# Create a list of all visible text controls
txt_ctrls = [ctrl for ctrl in all_ctrls if ctrl.can_be_label and ctrl.is_visible() and ctrl.window_text()]
# Build a dictionary of disambiguated list of control names
name_ctrl_id_map = findbestmatch.UniqueDict()
for index, ctrl in enumerate(all_ctrls):
ctrl_names = findbestmatch.get_control_names(ctrl, all_ctrls, txt_ctrls)
for name in ctrl_names:
name_ctrl_id_map[name] = index
# Swap it around so that we are mapped off the control indices
ctrl_id_name_map = {}
for name, index in name_ctrl_id_map.items():
ctrl_id_name_map.setdefault(index, []).append(name)
This shows that .descendants() isn't a method of the Application class, but belongs to the control. I was wrong there it seems. Is it possible to create my own version of print_control-identifiers() which returns a list of control objects that can be iterated through?

Correct method to list top-level windows is application.windows(). Then you can call .descendants() for every listed window. In the most cases application has only one top-level window. Particularly for backend="uia" even new dialogs are children of the main window (for backend="win32" every dialog is a top-level window).

Related

Is there a way to create nested class attributes in Python?

I've been struggling with creating a class for my image processing code in Python.
The code requires a whole bunch of different parameters (set in a params.txt file) which can easily be grouped into different categories. For example, some are paths, some are related to the experimental geometry, some are just switches for turning certain image processing features on/off etc etc.
If my "structure" (not sure how I should create it yet) is created as P, I would like to have something like,
P = my_param_object()
P.load_parameters('path/to/params.txt')
and then, from the main code, I can access whatever elements I need like so,
print(P.paths.basepath())
'/some/path/to/data'
print(P.paths.year())
2019
print(P.filenames.lightfield())
'andor_lightfield.dat'
print(P.geometry.dist_lens_to_sample())
1.5
print(P.settings.subtract_background())
False
print(P.settings.apply_threshold())
True
I already tried creating my own class to do this but everything is just in one massive block. I don't know how to create nested parts for the class. So for example, I have a setting and a function called "load_background". This makes sense because the load_background function always loads a specific filename in a specific location but should only do so if the load_background parameter is set to True
From within the class, I tried doing something like
self.setting_load_background = False
def method_load_background(self):
myutils.load_dat(self.background_fname())
but that's very ugly. It would be nicer to have,
if P.settings.load_background() == True:
P.load_background()
else:
P.generate_random_background()

How do I merge two lxml.objectify elements?

I am parsing some XML configuration to use the settings therein. I am not going to write out the XML again, so my interest here is only in extraction.
I have two lxml.objectify elements: On the one hand an element containing default global settings, on the other one containing instance-specific settings. Each element is similarly structured (e.g. root.global_settings.lights holds the same kind of settings as root.instance_settings.lights) but there can be intersections as well as set differences. The elements contain some children text nodes but also nodes containing other nodes.
What I want: A single element containing all settings from both elements. Instance-specific settings override global ones.
The best solution I currently have is looping over the instance children and overwriting/adding to the global ones (at all levels where there are text nodes). I was thinking maybe there would be something more like dict.update?
EDIT: Just to give an example
<global>
<birthday>Unknown</birthday>
<wishes>
<cake>Chocolate</cake>
<gift>Book</gift>
</wishes>
</global>
<instance>
<name>Mike</name>
<birthday>06-06-1974</birthday>
<wishes>
<notes>Hates flowers</notes>
</wishes>
<instance>
would yield the same as if I had run objectify.parse on
<global>
<name>Mike</name>
<birthday>06-06-1974</birthday>
<wishes>
<cake>Chocolate</cake>
<gift>Book</gift>
<notes>Hates flowers</notes>
</wishes>
</global>
I didn't find any 'native' lxml solutions. Objectify elements have traits from both dictionaries (you can access their values like a dict) and lists (you can extend and append an element with other elements). Update, however, does not work at all and extend has severe limitations, including the lack of recursiveness.
So I put together this recursive function that updates one element using another. The specific context here is using user settings to override default settings, leaving defaults in place where there are no user settings.
In essence the function distinguishes between four kinds of nodes defined by two characteristics:
1) Is the node absent in the default settings? If so we can just copy over (append) the user one.
2) If the node is also found in the default settings, we need to make a further distinction: Is it a DataElement - i.e. a node with a direct data value, e.g. <name>Mike</name> - or more of a 'structural' node without a direct data value, e.g. <wishes>...</wishes> in the example above. In the first case we replace the default node (and value) with the user one. In the second, we need to go one level deeper and repeat the whole procedure.
def merge(user_el, default_el):
'''Updating one lxml objectify elements with another'''
for child in user_el.iterchildren():
default_child = default_el.find(child.tag)
if default_child is None:
default_el.append(child)
continue
if isinstance(child, objectify.ObjectifiedDataElement):
default_el.replace(default_child, child)
elif isinstance(child, objectify.ObjectifiedElement):
merge(child, default_child)
EDIT: Testing the above made me realise that if a structural user element that also existed in the defaults, e.g. as an empty node, had multiple child nodes with the same tag name, they would gradually replace each other's data child nodes. To avoid that I created a version that edits a copy of the default settings. That way we continue to check against the empty placeholder element rather than the element we're gradually filling.
new_xml = copy.deepcopy(DEFAULT_XML)
merge(user_xml, new_xml, DEFAULT_XML)
def merge(user_el, new_el, default_el):
'''Updating one lxml objectify elements with another'''
for child in user_el.iterchildren():
new_child = new_el.find(child.tag)
default_child = default_el.find(child.tag)
if default_child is None:
new_el.append(child)
continue
if isinstance(child, objectify.ObjectifiedDataElement):
new_el.replace(new_child, child)
elif isinstance(child, objectify.ObjectifiedElement):
merge(child, new_child, default_child)

How do I separate the functions from the gui class in PySide?

In my PySide project I have 3 files:
one that contains all the gui stuff converted to python from Qt Designer,
another which has the signals, all the logic and functions and
one more that starts the whole application.
I think it's better to separate the logic from the functions.
The following is a simple function inserting items in a tablewidget:
# my_functions.py
def fill_table():
for row in range(10):
for col in range(10):
item_value = "...."
item = QtGui.QTableWidgetItem()
item.setText(str(item_value))
table_widget.setItem(row, col, item)
My main problem is how would you reference a widget from your application in a separate module.
Assuming my_functions.py is second item in your list of files, it appears that fill function uses a module-global table_widget. I don't recommend that but if that's really what you want, then just access it. So with
# my_functions.py
table_widget = ... # somewhere in that file, not shown in your post
def fill_table():
...populate table_widget...
Then:
# main.py
import my_functions
...
... somewhere, use my_functions.table_widget...
Better would be to define a custom class in my_functions.py, instantiate in main.py, and make fill_table() a method on custom class:
# my_functions.py
class MyTableWidget(QTableWidget):
...
def fill(self):
for row in range(10):
for col in range(10):
item_value = "...."
item = QtGui.QTableWidgetItem()
item.setText(str(item_value))
self.setItem(row, col, item)
# main.py
from my_functions import MyTableWidget
table = MyTableWidget()
table.fill()
There are lots of ways, but basically it seems that your current design is procedural rather than object-oriented. Nothing wrong with that, but you will find it rather clashes with the rest of PyQt and Python after a while, and is not as easy to maintain and debug once your app passes the stage of prototyping. So I recommend second approach.
The objects in a typical Qt application are connected together in parent/child relationships. Most often, there is a top-level main-window which functions as the root object, with all the other objects (widgets, layouts, etc) arranged in a hierarchy below it.
Given this, it is very natural to put all the gui-related program logic in the main-window class, because all the other objects will then be accessible via self. But if you put all the gui-related logic into functions in separate modules, there is no self available. So it would be up to you to provide that missing functionality.
The most obvious way to do this would be to keep a reference to the top-level window in the module that starts the application, so that the other modules can import it:
from app_module import main_window
# my_functions.py
def fill_table():
for row in range(10):
for col in range(10):
item_value = "...."
item = QtGui.QTableWidgetItem()
item.setText(str(item_value))
main_window.table_widget.setItem(row, col, item)
Alternatively, you could re-design all the functions so that they operate on only one object (or class of objects), and then explicitly pass in an instance:
# my_functions.py
def fill_table(table_widget):
...
However, whatever way you do it, it's hard to see how this could ever be the "best" way to structure the code.
Most gui applications consist of several largish sub-components that work more-or-less independently of one another, along with a central controller/manager that organises them all into a single functioning unit. The sub-components will usually be widget sub-classes (probably living in separate modules), which will become children of a main-window which may also function as the controller/manager. Organising your code along these lines is much more in line with the way Qt is designed to work, and will automatically avoid most of the potential communication problems between the various parts of the application.

Loop doesn't work, 3-lines python code

this question is about blender, python scripting
I'm completely new in this, so please excuse me for any stupid/newbie question/comment.
I made it simple (3 lines code) to make it easy addressing the problem.
what I need is a code that adds a new uv map for each object within loop function.
But this code instead is adding multiple new UV maps to only one object.
import bpy
for x in bpy.context.selected_objects:
bpy.ops.mesh.uv_texture_add()
what's wrong I'm doing here??
Thanks
Similar to what Sambler said, I always use:
for active in bpy.context.selected_objects:
bpy.context.scene.objects.active = active
...
These two lines I use more than any other when programming for Blender (except import bpy of course).
I think I first learned this here if you'd like a good intro on how this works:
https://cgcookiemarkets.com/2014/12/11/writing-first-blender-script/
In the article he uses:
# Create a list of all the selected objects
selected = bpy.context.selected_objects
# Iterate through all selected objects
for obj in selected:
bpy.context.scene.objects.active = obj
...
His comments explain it pretty well, but I will take it a step further. As you know, Blender lacks built-in multi-object editing, so you have selected objects and one active object. The active object is what you can and will edit if you try to set its values from python or Blender's gui itself. So although we are writing it slightly differently each time, the effect is the same. We loop over all selected objects with the for active in bpy.context.selected_objects, then we set the active object to be the next one in the loop that iterates over all the objects that are selected with bpy.context.scene.objects.active = active. As a result, whatever we do in the loop gets done once for every object in the selection and any operation we do on the object in question gets done on all of the objects. What would happen if we only used the first line and put our code in the for loop?
for active in bpy.context.selected_objects:
...
Whatever we do in the loop gets done once for every object in the selection but any operation we do on the object in question gets done on only the active object, but as many times as there are selected objects. This is why we need to set the active object from within the loop.
The uv_texture_add operator is one that only works on the current active object. You can change the active object by setting scene.objects.active
import bpy
for x in bpy.context.selected_objects:
bpy.context.scene.objects.active = x
bpy.ops.mesh.uv_texture_add()
note: I am not really familiar with blender
It seems that bpy.ops operations depend on the state of bpy.context. The context can also be overridden per-operation.
I assume that uv_texture_add() only works on a single object at a time?
Try something like this:
import bpy
for x in bpy.context.selected_objects:
override = { "selected_objects": x }
bpy.ops.mesh.uv_texture_add(override)
That should run the operations as if only one object was selected at a time.
Source:
https://www.blender.org/api/blender_python_api_2_63_17/bpy.ops.html#overriding-context

How to programatically create a detailed event like z3c.form does?

I have a simple event handler that looks for what has actually been changed (it's registered for a IObjectModifiedEvent events), the code looks like:
def on_change_do_something(obj, event):
modified = False
# check if the publication has changed
for change in event.descriptions:
if change.interface == IPublication:
modified = True
break
if modified:
# do something
So my question is: how can I programmatically generate those descriptions? I'm using plone.app.dexterity everywhere, so z3c.form is doing that automagically when using a form, but I want to test it with a unittest.
event.description is nominally an IModificationDescription object, which is essentially a list of IAttributes objects: each Attributes object having an interface (e.g. schema) and attributes (e.g. list of field names) modified.
Simplest solution is to create a zope.lifecycleevent.Attributes object for each field changed, and pass as arguments to the event constructor -- example:
# imports elided...
changelog = [
Attributes(IFoo, 'some_fieldname_here'),
Attributes(IMyBehaviorHere, 'some_behavior_provided_fieldname_here',
]
notify(ObjectModifiedEvent(context, *changelog)
I may also misunderstood something, but you may simple fire the event in your code, with the same parameters like z3c.form (Similar to the comment from #keul)?
After a short search in a Plone 4.3.x, I found this in z3c.form.form:
def applyChanges(self, data):
content = self.getContent()
changes = applyChanges(self, content, data)
# ``changes`` is a dictionary; if empty, there were no changes
if changes:
# Construct change-descriptions for the object-modified event
descriptions = []
for interface, names in changes.items():
descriptions.append(
zope.lifecycleevent.Attributes(interface, *names))
# Send out a detailed object-modified event
zope.event.notify(
zope.lifecycleevent.ObjectModifiedEvent(content, *descriptions))
return changes
You need two testcases, one which does nothing and one which goes thru your code.
applyChanges is in the same module (z3c.form.form) it iterates over the form fields and computes a dict with all changes.
You should set a break point there to inspect how the dict is build.
Afterwards you can do the same in your test case.
This way you can write readable test cases.
def test_do_something_in_event(self)
content = self.get_my_content()
descriptions = self.get_event_descriptions()
zope.event.notify(zope.lifecycleevent.ObjectModifiedEvent(content, *descriptions))
self.assertSomething(...)
IMHO mocking whole logic away may be a bad idea for future, if the code changes and probably works completely different, your test will be still fine.

Categories

Resources