setSelected in QTreeWidget - python

I have a project where I need to change the selection of a tree widget in code. This needs to be done after I clear out the tree and populate it again.
I'm trying to mark the appropriate item as "selected" while I'm adding them. This works for root level nodes. But for child nodes, it doesn't. I need to store the QTreeWidgetItem in another variable and mark it as selected after the tree has been completely populated. Why does this happen?
This does not work:
def refreshTree(self):
treeObj.clear()
for item in items:
temp = QTreeWidgetItem(0)
for key, val in item.subitems().items():
childTemp = QTreeWidgetItem(0)
...setup text, font, etc...
if(condition1):
childTemp.setSelected(True)
temp.addChild(childTemp)
if(!condition1 and condition2):
temp.setSelected(True)
treeObj.addToplevelItem(temp)
This does:
def refreshTree(self):
treeObj.clear()
for item in items:
temp = QTreeWidgetItem(0)
for key, val in item.subitems().items():
childTemp = QTreeWidgetItem(0)
...setup text, font, etc...
if(condition1):
selTemp = childTemp
temp.addChild(childTemp)
if(!condition1 and condition2):
temp.setSelected(True)
elif(selTemp):
selTemp.setSelected(True)
treeObj.addToplevelItem(temp)

It is not specified in the documentation, but setSelected does nothing if the item hasn't been added to a view yet:
inline void QTreeWidgetItem::setSelected(bool aselect)
{ if (view) view->setItemSelected(this, aselect); }
So, you should either
pass treeObj or temp in the constructor of your QTreeWidgetItem to make them part of the view from the start
or call addChild/addTopLevelItem before calling setSelected (or other functions like setExpanded...).
I don't know why your second code was even working.

Related

How to add a state to a Selection field and show it ordered in the statusbar in Odoo 11?

I have added a new state to a Selection field, using the parameter selection_add:
state = fields.Selection(
selection_add=[
('draft_ok', 'Validated Quotation'),
],
)
Now I want to show it in the XML view, where currently state is shown this way:
<field name="state" widget="statusbar" statusbar_visible="draft,sent,sale"/>
If I inherit from that view, to add the new state:
<xpath expr="//header/field[#name='state']" position="attributes">
<attribute name="statusbar_visible">draft,draft_ok,sent,sale</attribute>
</xpath>
The new state is shown at the end of the status bar. I want to show it between draft and sent states.
The only way I know to do that is redefining the states in Python:
state = fields.Selection(
selection=[
('draft', 'Quotation'),
('draft_ok', 'Validated Quotation'),
('sent', 'Quotation Sent'),
('sale', 'Sales Order'),
('done', 'Locked'),
('cancel', 'Cancelled'),
],
)
But this solution is not very consistent, because if other module also adds a state to this field and my module code is executed after it, I would destroy the state added by this other module.
So I am looking for other way to show the statusbar with a customised order. Any ideas?
From the code responsible of this witch located in fields.Selection class There is no way to do it
without using special tricks:
# frame code
def _setup_attrs(self, model, name):
super(Selection, self)._setup_attrs(model, name)
# determine selection (applying 'selection_add' extensions)
for field in reversed(resolve_mro(model, name, self._can_setup_from)):
# We cannot use field.selection or field.selection_add here
# because those attributes are overridden by ``_setup_attrs``.
if 'selection' in field.args:
self.selection = field.args['selection']
if 'selection_add' in field.args:
# use an OrderedDict to update existing values
selection_add = field.args['selection_add']
self.selection = OrderedDict(self.selection + selection_add).items()
Like for example monkey patching I tried normal inheritance It didn't work I think It needs a lot of work.
This what I tried and it worked just fine in Odoo 9. I created a new key is selection_add_after witch is a dictionary
1. key is the value of selection that you want to add item after it
2. value is the list of selection items that you want to add
def _setup_attrs(self, model, name):
super(fields.Selection, self)._setup_attrs(model, name)
# determine selection (applying 'selection_add' extensions)
for field in reversed(fields.resolve_mro(model, name, self._can_setup_from)):
# We cannot use field.selection or field.selection_add here
# because those attributes are overridden by ``_setup_attrs``.
if 'selection' in field.args:
self.selection = field.args['selection']
if 'selection_add' in field.args:
# use an OrderedDict to update existing values
selection_add = field.args['selection_add']
self.selection = OrderedDict(self.selection + selection_add).items()
if 'selection_add_after' in field.args:
selection_add_atfer = field.args['selection_add_after']
new_selection = []
for item in self.selection:
new_selection.append(item) # add the element firs
items_to_add = selection_add_atfer.get(item[0], [])
for item_to_add in items_to_add: # then add the element if there is
new_selection.append(item_to_add)
# I don't know why they used OrderdedDict ???!! do you have any idea why?!!
self.selection = OrderedDict(new_selection).items()
# mucky patch the method in selection field
fields.Selection._setup_attrs = _setup_attrs
Make sure you patch before defining the field
# add element after draft
state = fields.Selection(selection_add_after={'draft': [('hello', 'Hello')]})
# add element after draft and other emelent after confirmed
state = fields.Selection(selection_add_after={'draft': [('hello', 'Hello')], 'confirmed': [('test','Test')]})
You can add new key like removing or anything you want.
But monkey patching Framework method is also a bad idea because if there is any updates in the _setup_attrs is always
removed by this.
EDIT
For Odoo 11, this is the code:
def _setup_attrs(self, model, name):
super(fields.Selection, self)._setup_attrs(model, name)
# determine selection (applying 'selection_add' extensions)
for field in reversed(fields.resolve_mro(model, name, self._can_setup_from)):
# We cannot use field.selection or field.selection_add here
# because those attributes are overridden by ``_setup_attrs``.
if 'selection' in field.args:
self.selection = field.args['selection']
if 'selection_add' in field.args:
# use an OrderedDict to update existing values
selection_add = field.args['selection_add']
self.selection = list(OrderedDict(self.selection + selection_add).items())
if 'selection_add_after' in field.args:
selection_add_atfer = field.args['selection_add_after']
new_selection = []
for item in self.selection:
new_selection.append(item) # add the element firs
items_to_add = selection_add_atfer.get(item[0], [])
for item_to_add in items_to_add: # then add the element if there is
new_selection.append(item_to_add)
# I don't know why they used OrderdedDict ???!! do you have any idea why?!!
self.selection = list(OrderedDict(new_selection).items())
fields.Selection._setup_attrs = _setup_attrs
try this way :
state = fields.Selection(
selection_add=[
('draft_ok', 'Validated Quotation'),(sent,)
],
)

Convert pango markup string to GtkTextTag properties

I've got a gtk.TextView that I'd like to add markup-like text to. I know this can be achieved through the use of gtk.TextTag which you can create with similar properties as a pango markup string. I noticed there is no easy way to just say set_markup to a gtk.TextBuffer much like you can with multiple other widgets. Instead you have to create a TextTag, give it properties, and then insert it into the TextBuffer's TagTable specifying the iters that the tag applies to.
I'd ideally like to create a function that can convert a pango markup string into a TextTag to get the same effect. But gtk doesn't appear to have that functionality built-in.
I've noticed that you can use pango.parse_markup() on a marked up string and it will create a pango.AttributeList which contains information regarding the properties set on the string and the indices that they occur at. But there are slight differences in each type of attribute that make it difficult to generalize for every case. Is there a better way to go about this? Or is pango markup just not meant to be converted into gtk.TextTag's?
I finally worked out my own solution to this problem. I created a function that parses the markup string (using pango.parse_markup). Through reading the documentation and python introspection, I was able to work out how to take pango.Attribute and turn convert it into properties that a GtkTextTag can use.
Here's the function:
def parse_markup_string(string):
'''
Parses the string and returns a MarkupProps instance
'''
#The 'value' of an attribute...for some reason the same attribute is called several different things...
attr_values = ('value', 'ink_rect', 'logical_rect', 'desc', 'color')
#Get the AttributeList and text
attr_list, text, accel = pango.parse_markup( string )
attr_iter = attr_list.get_iterator()
#Create the converter
props = MarkupProps()
props.text = text
val = True
while val:
attrs = attr_iter.get_attrs()
for attr in attrs:
name = attr.type
start = attr.start_index
end = attr.end_index
name = pango.AttrType(name).value_nick
value = None
#Figure out which 'value' attribute to use...there's only one per pango.Attribute
for attr_value in attr_values:
if hasattr( attr, attr_value ):
value = getattr( attr, attr_value )
break
#There are some irregularities...'font_desc' of the pango.Attribute
#should be mapped to the 'font' property of a GtkTextTag
if name == 'font_desc':
name = 'font'
props.add( name, value, start, end )
val = attr_iter.next()
return props
This function creates a MarkupProps() object that has the ability to generate GtkTextTags along with the index in the text to apply them to.
Here's the object:
class MarkupProps():
'''
Stores properties that contain indices and appropriate values for that property.
Includes an iterator that generates GtkTextTags with the start and end indices to
apply them to
'''
def __init__(self):
'''
properties = ( {
'properties': {'foreground': 'green', 'background': 'red'}
'start': 0,
'end': 3
},
{
'properties': {'font': 'Lucida Sans 10'},
'start': 1,
'end':2,
},
)
'''
self.properties = []#Sequence containing all the properties, and values, organized by like start and end indices
self.text = ""#The raw text without any markup
def add( self, label, value, start, end ):
'''
Add a property to MarkupProps. If the start and end indices are already in
a property dictionary, then add the property:value entry into
that property, otherwise create a new one
'''
for prop in self.properties:
if prop['start'] == start and prop['end'] == end:
prop['properties'].update({label:value})
else:
new_prop = {
'properties': {label:value},
'start': start,
'end':end,
}
self.properties.append( new_prop )
def __iter__(self):
'''
Creates a GtkTextTag for each dict of properties
Yields (TextTag, start, end)
'''
for prop in self.properties:
tag = gtk.TextTag()
tag.set_properties( **prop['properties'] )
yield (tag, prop['start'], prop['end'])
So with this function and the MarkupProps object, I am able to, given a pango markup string, breakdown the string into it's properties, and text form, and then convert that into GtkTextTags.
Haven't followed GTK+ development, maybe they added something lately, but see these bugs: #59390 and #505478. Since they are not closed, likely nothing is done.

Search and remove element with elementTree in Python

I have an XML document in which I want to search for some elements and if they match some criteria
I would like to delete them
However, I cannot seem to be able to access the parent of the element so that I can delete it
file = open('test.xml', "r")
elem = ElementTree.parse(file)
namespace = "{http://somens}"
props = elem.findall('.//{0}prop'.format(namespace))
for prop in props:
type = prop.attrib.get('type', None)
if type == 'json':
value = json.loads(prop.attrib['value'])
if value['name'] == 'Page1.Button1':
#here I need to access the parent of prop
# in order to delete the prop
Is there a way I can do this?
Thanks
You can remove child elements with the according remove method. To remove an element you have to call its parents remove method. Unfortunately Element does not provide a reference to its parents, so it is up to you to keep track of parent/child relations (which speaks against your use of elem.findall())
A proposed solution could look like this:
root = elem.getroot()
for child in root:
if child.name != "prop":
continue
if True:# TODO: do your check here!
root.remove(child)
PS: don't use prop.attrib.get(), use prop.get(), as explained here.
You could use xpath to select an Element's parent.
file = open('test.xml', "r")
elem = ElementTree.parse(file)
namespace = "{http://somens}"
props = elem.findall('.//{0}prop'.format(namespace))
for prop in props:
type = prop.get('type', None)
if type == 'json':
value = json.loads(prop.attrib['value'])
if value['name'] == 'Page1.Button1':
# Get parent and remove this prop
parent = prop.find("..")
parent.remove(prop)
http://docs.python.org/2/library/xml.etree.elementtree.html#supported-xpath-syntax
Except if you try that it doesn't work: http://elmpowered.skawaii.net/?p=74
So instead you have to:
file = open('test.xml', "r")
elem = ElementTree.parse(file)
namespace = "{http://somens}"
search = './/{0}prop'.format(namespace)
# Use xpath to get all parents of props
prop_parents = elem.findall(search + '/..')
for parent in prop_parents:
# Still have to find and iterate through child props
for prop in parent.findall(search):
type = prop.get('type', None)
if type == 'json':
value = json.loads(prop.attrib['value'])
if value['name'] == 'Page1.Button1':
parent.remove(prop)
It is two searches and a nested loop. The inner search is only on Elements known to contain props as first children, but that may not mean much depending on your schema.
I know this is an old thread but this kept popping up while I was trying to figure out a similar task. I did not like the accepted answer for two reasons:
1) It doesn't handle multiple nested levels of tags.
2) It will break if multiple xml tags are deleted in the same level one-after-another. Since each element is an index of Element._children you shouldn't delete while forward iterating.
I think a better more versatile solution is this:
import xml.etree.ElementTree as et
file = 'test.xml'
tree = et.parse(file)
root = tree.getroot()
def iterator(parents, nested=False):
for child in reversed(parents):
if nested:
if len(child) >= 1:
iterator(child)
if True: # Add your entire condition here
parents.remove(child)
iterator(root, nested=True)
For the OP, this should work - but I don't have the data you're working with to test if it's perfect.
import xml.etree.ElementTree as et
file = 'test.xml'
tree = et.parse(file)
namespace = "{http://somens}"
props = tree.findall('.//{0}prop'.format(namespace))
def iterator(parents, nested=False):
for child in reversed(parents):
if nested:
if len(child) >= 1:
iterator(child)
if prop.attrib.get('type') == 'json':
value = json.loads(prop.attrib['value'])
if value['name'] == 'Page1.Button1':
parents.remove(child)
iterator(props, nested=True)
A solution using lxml module
from lxml import etree
root = ET.fromstring(xml_str)
for e in root.findall('.//{http://some.name.space}node'):
parent = e.getparent()
for child in parent.find('./{http://some.name.space}node'):
try:
parent.remove(child)
except ValueError:
pass
Using the fact that every child must have a parent, I'm going to simplify #kitsu.eb's example. f using the findall command to get the children and parents, their indices will be equivalent.
file = open('test.xml', "r")
elem = ElementTree.parse(file)
namespace = "{http://somens}"
search = './/{0}prop'.format(namespace)
# Use xpath to get all parents of props
prop_parents = elem.findall(search + '/..')
props = elem.findall('.//{0}prop'.format(namespace))
for prop in props:
type = prop.attrib.get('type', None)
if type == 'json':
value = json.loads(prop.attrib['value'])
if value['name'] == 'Page1.Button1':
#use the index of the current child to find
#its parent and remove the child
prop_parents[props.index[prop]].remove(prop)
I also used XPath for this issue, but in a different way:
root = elem.getroot()
elementName = "YourElement"
#this will find all the parents of the elements with elementName
for elementParent in root.findall(".//{}/..".format(elementName)):
#this will find all the elements under the parent, and remove them
for element in elementParent.findall("{}".format(elementName)):
elementParent.remove(element)
I like to use an XPath expression for this kind of filtering. Unless I know otherwise, such an expression must be applied at the root level, which means I can't just get a parent and apply the same expression on that parent. However, it seems to me that there is a nice and flexible solution that should work with any supported XPath, as long as none of the sought nodes is the root. It goes something like this:
root = elem.getroot()
# Find all nodes matching the filter string (flt)
nodes = root.findall(flt)
while len(nodes):
# As long as there are nodes, there should be parents
# Get the first of all parents to the found nodes
parent = root.findall(flt+'/..')[0]
# Use this parent to remove the first node
parent.remove(nodes[0])
# Find all remaining nodes
nodes = root.findall(flt)
I would like only to add a comment on the accepted answer, but my lack of reputation doesn't allow me to. I wanted to add that it is important to add .findall("*")to the iterator to avoid issues, as stated in the documentation:
Note that concurrent modification while iterating can lead to problems, just like when iterating and modifying Python lists or dicts. Therefore, the example first collects all matching elements with root.findall(), and only then iterates over the list of matches.
Therefore, in the accepted answer the iteration should be for child in root.findal("*"):instead of for child in root:. Not doing so made my code skip some elements from the list.

wx.TreeCtrl item

I'm trying to use a TreeCtrl to represent a folder structure. For each folder I need to know it's absolute path and name. I'm currently doing something like this:
self.root = self.tree.AddRoot(project.name)
self.tree.SetPyData(self.root, None)
self.root.path = root
---- other code -----
childItem = self.tree.AppendItem(self.root, child.name)
childItem.path = self.root.path + "/" + child.name
But now on an event I will need to get the path string. So far my approach that fails is:
self.Bind(wx.EVT_TREE_ITEM_EXPANDED, self.OnItemExpanded, self.tree)
----- other code -------
def OnItemExpanded(self, evt):
selected = evt.GetItem()
print selected.path
Now this fails because: AttributeError: 'TreeItemId' object has no attribute 'path' . From what I understand here the event only gives me a Id to a Item from the tree and not the actual Item that resulted from the "childItem = self.tree.AppendItem(self.root, child.name)" ? If that is the case how can I get to that item ?
regards,
Bogdan
What is the .path property? Is this something you are creating or an actual member of the TreeItemId object (this is the object returned from the "AppendItem" method)? I do not see any docs on it.
If you want to store arbitrary data in the child items use SetPyData/GetPyData methods.
childItem = self.tree.AppendItem(self.root, child.name)
self.tree.SetPyData(childItem, ["hi", "i" , "am", "a", "python", "object"])
Then in your handler:
def OnItemExpanded(self, event):
item = event.GetItem()
if item:
pyObj = self.tree.GetPyData(item)

trouble setting up GtkTreeViews in PyGtk

I've got some code in a class that extends gtk.TreeView, and this is the init method. I want to create a tree view that has 3 columns. A toggle button, a label, and a drop down box that the user can type stuff into. The code below works, except that the toggle button doesn't react to mouse clicks and the label and the ComboEntry aren't drawn. (So I guess you can say it doesn't work). I can add rows just fine however.
#make storage enable/disable label user entry
self.tv_store = gtk.TreeStore(gtk.ToggleButton, str, gtk.ComboBoxEntry)
#make widget
gtk.TreeView.__init__(self, self.tv_store)
#make renderers
self.buttonRenderer = gtk.CellRendererToggle()
self.labelRenderer = gtk.CellRendererText()
self.entryRenderer = gtk.CellRendererCombo()
#make columns
self.columnButton = gtk.TreeViewColumn('Enabled')
self.columnButton.pack_start(self.buttonRenderer, False)
self.columnLabel = gtk.TreeViewColumn('Label')
self.columnLabel.pack_start(self.labelRenderer, False)
self.columnEntry = gtk.TreeViewColumn('Data')
self.columnEntry.pack_start(self.entryRenderer, True)
self.append_column(self.columnButton)
self.append_column(self.columnLabel)
self.append_column(self.columnEntry)
self.tmpButton = gtk.ToggleButton('example')
self.tmpCombo = gtk.ComboBoxEntry(None)
self.tv_store.insert(None, 0, [self.tmpButton, 'example label', self.tmpCombo])
First of all, you need to create a model with bool, str and str columns, not the way you are doing now. Second, you need to bind properties of renderers from appropriate model columns, e.g. as in
self.columnButton = \
gtk.TreeViewColumn ('Enabled', self.buttonRenderer,
active = 0) # 0 is the tree store column index
Then you need to set editable property on the renderer to True. And finally, you need to handle signals (changed or editing-done, depending on renderer type) yourself and update the store accordingly.
It may be easier to use some helpers, e.g.
Py-gtktree — there's even an example for editing a tree there.
Just connnect the toggled signal in the gtk.CellRendererToggle, when you click on it, it will emit that signal, then in your callback change the value in the model.
ej.
def toggle(self, cellrenderer, path):
Self.model[path][column] = not self.model[path][column]
self.model is the model asociated to the treeview,

Categories