I have a QTreeWidget structure like this:
root
|
groups
|
tables
I am trying to do something when there is a "doubleClick" but ONLY in the the childs called "tables".
The only good result that I have been able to get is:
self.ui.treeWidget.itemDoubleClicked.connect(self.treeWidgetItemAction)
The problem now is that ALL the elements in the QTreeWidget are responding to the "doubleClick". I do not want that, but I do not know how to reference only the children in the final branch of the tree.
Any help??
Thanks.
Just make your treeWidgetItemAction method check whether the doubleCliked item is one that should react on doubleClicks, and only do what you want to do in that case.
To know where an item is a leaf, you could check it's childCount method. If it doesn't have any children, it's a leaf.
Related
I have a QTreeWidget with the following shape:
Root
- Item1
-- Sub11
--- Sub111
-- Sub12
--- Sub121
...
When double clicking on any item from the QTreeWidget the selection expands by default (or collapsed if it was already expanded). I created a simple function tree_get_details(self, item, column) to print item and column of the double clicked item and connected it with itemDoubleClicked. This works nicely so far. However how can I make this function available only at bottom layer of the tree (ie for subXXX rows). Hence for Root, Item1, Item2, Sub11, Sub12 just the default behaviour for the double clicked event and for Sub111, Sub121 printing item and column?
Somehow I need to test in my function tree_get_details if the "item" passed in argument is at the bottom layer. I know of the topLevelItem method but can't find the corresponding bottom one (if it exists...). My tree has 4-level hierarchy.
It depends on what exactly you mean with "bottom item".
The usual name for a "bottom item" in a tree is "leaf item".
It is defined as an item without children.
That's something you can check with the QTreeWidgetItem::childCount method (it's a leaf if and only if it returns 0).
If for whatever reason, you want to detect items in the third sub-level, you can count the number of parent items recursively.
That's slightly more complicated and probably not what you want.
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).
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)
I have a TreeView widget that I inserted a few items into, and applied a few tags selectively on some of those items. Now I want to bind a click event to all items in the widget, but the bind syntax looks like this:
treeView.tag_bind(tag_name, event_sequence, click_handler)
My problem is I want to do this for all tags, and also for non-tagged items. Is there something like .tag_bind_all?
Use a list or function. Assuming to you want to bind all tags the same:
for this_tag in [tag_name1, tag_name2, tag_name3]:
treeView.tag_bind(this_tag, event_sequence, click_handler)
I figured this out, this did it:
treeView.bind('<<TreeviewSelect>>', lambda *x:self.__treeViewItemSelected())
Then inside the method I can do :
item_id = treeView.focus()
value = treeView.item(item_id)
I have a wx frame where I have a quite a few checkboxes. Ever so often when the user changes the settings in a drop down menu (wx.ComboBox) I'd like to clear all the checkboxes. Currently, I've implemented a method that gets called when a change in the ComboBox happens and it clears each check box manually, i.e.:
def ClearCheckBoxes(self):
self.cb_EnableControl.SetValue(0)
self.cb_EnableRun.SetValue(0)
self.cb_EnablePower.SetValue(0)
...
...
Although I only have about 10 of these, my ClearCheckBoxes method would be much cleaner if it were something like this:
def ClearCheckBoxes(self):
for CheckBox in self.AllCheckBoxes:
CheckBox.SetValue(0)
Also, I suppose I could create a list (i.e. AllCheckBoxes) and add all the checkboxes to the list as I create them, and then it would only be a matter of iterating through the list. But the point here is that I'd like to know if there was an pre-defined way of doing this.
Thanks
for control in self.GetChildren():
if isinstance(control, wx.CheckBox):
control.SetValue(False)
Have you tried something super ugly like:
[checkbox.SetValue(0) for checkbox in dir(self) where type(checkbox) == type(wx.Checkbox)]