I have a number of items in a QTreeView. Each item is generated using this class:
class Branch(QStandardItem):
def __init__(self, label, uri = None):
QStandardItem.__init__(self, label)
self.uri = uri
This is my actual tree:
class FileTree(QTreeView):
def __init__(self):
QTreeView.__init__(self)
def keyPressEvent(self, event):
if event.key() == Qt.Key_Space or event.key() == Qt.Key_Return:
crawler = self.selectedIndexes()[0].model().item(self.selectedIndexes()[0].row())
print(crawler.uri)
QTreeView.keyPressEvent(self, event)
As you can see, I'm a little unsure as to how to get the uri variable from the selected item. I found that selectedIndexes() returns a model and not the item itself. I'm not sure how to get from one to the other. Trying to get the item number using self.selectedIndexes()[0].row() was a bit of a shot in the dark, but it seems to ignore the various branches in the tree (for instance, it will give me a 0 for the first row in a branch, but won't tell me anything about what branch it's in).
What's the proper way to get the selected item from the QTreeView? Or is there a better way of detecting the spacebar or return keys being hit that would make this easier? There's a severe lack of Python documentation for Qt, so it's really hard to know if I'm ever doing things in a sensical manner.
You are calling the right function, it actually returns a QModelIndexList which is just a typedef for QList<QModelIndex> with the QModelIndex being the data structure that can point to any part of the tree. QModelIndex is not a Model in the sense of Model View Controller (MVC) but an adress of an object in a QAbstractItemModel which is the datastructure under all of Qt's ItemView objects, including your tree. You are actually pretty close, QAbstractModelIndex consists of a row, a column and a parent, which lets it adress any position in a hierarchical data structure. If you use the line
index = self.selectedIndexes()[0]
crawler = index.model().itemFromIndex(index)
you should get to the object that you are looking for.
As for documentation, even though there is no python specific documentation it helps to read through the official Qt documentation, the class hierarchy and functionality is still the same. There is very little C++ specific information in the docs.
Harald's answer didn't work for me, because I'm using a QSqlQueryModel as the model (I got the error {AttributeError}'QSqlQueryModel' object has no attribute 'itemFromIndex').
The below did the trick for me though, to get the 0th column data of the selected row:
dbQueryModel.itemData(treeView.selectedIndexes()[0])
Related
I'm using pyqt5 and python 3.6.
I am confused about index functions in QModel/TreeView and ask here to clear my knot in brain. Some threads confused me more because they are very special. My question is more or less a basic one.
I have a model (based on Uludag's great tutorials) with data and did set it to my tree:
treeView = QTreeView()
treeView.show()
treeView.setModel(model)
#additional code
treeView.clicked.connect(treeViewStructure_clicked)
Now I have to prepare further action in the program based on the selected item. I can get the item by
def treeViewStructure_clicked(self):
#get the row of the click and print
index = treeView.currentIndex()
print(index.row())
print('selected item index at %s with data: %s' % (index.row(), index.data()))
So far so good. But the index is generated from treeView (model doesn't work). How can I connect the selection form the view to the model, so that I have the correct Modelindex and can change data in the model?
And of course, how to write them back that they will get synchronized? In other words, which index do I need for what and where and where do they come from?
I would love to leave a comment but I don't have enough reputation.
Since your implementation of model did not include a method that could return the selected index, I have to get it from pyqt5. Since treeView is a QTreeView, you can:
treeView.selectionModel().selectedIndexes()
the information is from here.
These will return a QModelIndex list, which has an api here.
(note that although there are code examples on this website, it's in C++, but it should still be readable)
In the api, you can get the row and column as well as parent from the QModelIndex object.
Normally, I'd just modify the data via the object returned by treeView.selectionModel().selectedIndexes().
for example:
arr = treeView.selectionModel().selectedIndexes()
index = arr[0]
then just modify index.
but if you MUST use your own model for whatever reason, there is one thing you can do.
Since it extends QAbstractItemModel, you can use the index function.
For example:
arr = treeView.selectionModel().selectedIndexes()
indexTemp = arr[0]
index = model.index(indexTemp.row(), indexTemp.column(), indexTemp.parent())
then modify the value through setData funtion
But as you can see, it's an extra step to get the same object
I am trying to make a listview with checkboxes that checks the selected boxes when the enter/return key is pressed. I do this with an override of the eventfilter for my MainWindow (yes I ought to subclass it, but I couldn't get that working)
In the eventfilter i get a None value returned from the itemFromIndex method even though I just passed the index through a .isValid() without problems. Obviously i am missing something, but i can't figure it out - is it looking at completely different indices? is the model not updated?
Any advice on alternate approaches are welcome
This is the method I use to fill the model (QStandardItemModel) with items, it's only called when i load a file.
def update_siNLV(self,names,model):
model.clear()
for name in names:
item = Qg.QStandardItem(name)
item.setCheckState(Qc.Qt.Unchecked)
item.setCheckable(True)
model.appendRow(item)
This is from the init method where I create a variable for the selectionmodel and install the eventfilter on my QListView
self.sigInSelection = self.siNLV.selectionModel()
self.siNLV.installEventFilter(self)
The eventFilter method looks like this and the filtering part of the method works (I've made it print the selected indices with press on the enter key)
def eventFilter(self,receiver,event):
if event.type() == QtCore.QEvent.KeyPress:
if event.key() == QtCore.Qt.Key_Return or event.key() == Qc.Qt.Key_Enter:
indexes = self.sigInSelection.selectedIndexes()
for index in indexes:
if index.isValid():
print(str(index.row())+" "+str(index.column()))
item = self.sigInModel.itemFromIndex(index)
item.setCheckState(qtCore.Qt.Checked)
return True
return super(form,self).eventFilter(receiver,event)
As discussed in the comments:
The indices returned by QItemSelectionModel.selectedIndexes() come from the view and relate to the connection between the view and its immediate model. The identity of that model can be found by calling QModelIndex.model() and in this case it is not the model that you want: it is instead a proxy model that is in-between your desired QStandardItemModel and the view.
To get to the model you want you need to use QAbstractProxyModel.mapToSource(). So you might use code something like this:
source_index = self.proxy.mapToSource(index)
item = self.sigInModel.itemFromIndex(source_index)
More generally you could traverse an arbitrary proxy structure and avoid this hard-coded usage of a single known proxy by code something like:
proxy_model = index.model()
while proxy_model != self.sigInModel:
index = proxy_model.mapToSource(index)
proxy_model = index.model()
item = self.sigInModel.itemFromIndex(index)
But this is probably overkill in this case where you know there is a simple single proxy.
I'm writing an exporter for a game my friend and I are making and it involves setting custom properties and tags to objects which are then recognized in the game and dealt with accordingly. Our engine, which is written in C/C++ has been successfully tested with my current version of the export script, and I''m currently working on tidying it up.
The script uses Blender's feature of custom properties to write custom tags to output file. The model typically consists of multiple 'parts' (Blender mesh objects that are parented to form a tree, with one 'parent' and multiple 'child' objects) and some of those parts are simple Blender Empty objects (for only it's X, Y and Z coordinates are needed) with custom properties that mark where things like ship's propulsion (it's a 3D shooter) are placed, or where the flames/explosions appear when ship's been shot. Those empty parts are also parented to either 'root' object or any of it's children. So far it's been working good, I have written a generic Operator class and some extended classes that reside in a panel which set part's properties (pretty handy since you don't have to add those custom properties by hand).
Now I want to speed thing up even more, that is to be able to simply click on an operator of desired type, and it should automatically add it to the scene and parent it to the active/selected object. I know how to do that, but I can't get those operators to change their labels. Basically, what I want is to operator to say 'Bullet point' when an existing empty is selected (I've got that part done), and when there's a mesh object selected to say 'Add bullet point'. So I just need a way to dynamically change operators' labels depending on the context (as the title of the question states clearly :))
This is what I got so far:
class OBJECT_OT_tg_generic (bpy.types.Operator):
bl_label = "Sets Generic Part Type"
bl_idname = "rbm.set_generic_part_type"
OB_TYPE = None
#classmethod
def poll (cls, context):
act = context.active_object
if 'Type' in act.keys ():
if act['Type'] == cls.OB_TYPE:
cls.bl_label = 'foo'
print (cls.bl_label)
# this prints foo but doesn't change the label
return False
return True
def execute (self, context):
# TODO: add code to automatically place empties and parent them to active object
bpy.context.active_object['Type'] = self.OB_TYPE
return{"FINISHED"}
And an example of a subclass:
class OBJECT_OT_tg_bullet_point (OBJECT_OT_tg_generic):
bl_label = "Bullet Point"
bl_idname = "rbm.set_bullet_point"
OB_TYPE = OT_BULLET_POINT
Here's how it looks in Blender:
http://i.imgur.com/46RAS.png
Guess I solved it. When you're adding an operator to a panel, you can do something like this:
def draw (self, context):
layout = self.layout
row = layout.row()
row.operator("foo.bar", text="Whatever you want")
and the "Whatever you want" is going to be your button's label. But what I did was something else. I didn't change the operators' labels, but instead gave them a different icons depending on whether it's a mesh or an empty currently selected/active:
def draw (self, context):
# (...) we're skipping some code here, obviously
act = context.active_object
if act.type == 'MESH':
op_icon = 'ZOOMIN'
else:
op_icon = 'EMPTY_DATA'
row = layout.column(align=True)
row.operator('rbm.set_bullet_point', icon=op_icon)
row.operator('rbm.set_rocket_point', icon=op_icon)
# (...) rest of the code
I am trying to code the following: Two Columns. One contains a itemId, the other one contains a typeId. I want to render the itemId only when the typeId equals a specific value.
class IDRenderer(gtk.CellRendererText):
def __init__(self):
gtk.CellRendererText.__init__(self)
def do_render(self,window, widget, background_area, cell_area, expose_area, flags):
if ----} Condition to ask for value of the typeId - Cell {-----:
gtk.CellRendererText.do_render(self, window, widget, background_area, cell_area,
expose_area, flags)
gobject.type_register(IDRenderer)
I don't know how to get the iter of the currently rendered row which i need to determine the value of the typeId. Is this even possible?
I now found out, thanks to a nice guy on #pygtk on gimpIRC:
You can do that, with binding so called cell data functions to the corresponding gtk.TreeViewColumn as done here in this example
def renderId(celllayout, cell, model, iter):
if model.get_value(iter,1) == 3:
cell.set_property('visible',True)
else:
cell.set_property('visible',False)
treeviewcolumn = gtk.TreeViewColumn()
renderer = gtk.CellRendererText()
treeviewcolumn.add_attribute(renderer,'text',0)
treeviewcolumn.set_cell_data_func(renderer,renderId)
I ommited some code relevant to render a complete treeview, but i think it shows what i wanted to do and how to do it.
The column renderes the value in the first column (0) of the model only if the value in the second modelcolumn (1) equals 3
I hope this could help someone some time.
It's not possible as far as I know. You need to use properties of the custom renderer which will be set automatically by the code calling the rendering function. (Like the text property of CellRendererText -- the rendering code doesn't get the text from the tree model, but the tree model sets the text property of the renderer before calling the rendering code.)
My question is if we can assign/bind some value to a certain item and hide that value(or if we can do the same thing in another way).
Example: Lets say the columns on ListCtrl are "Name" and "Description":
self.lc = wx.ListCtrl(self, -1, style=wx.LC_REPORT)
self.lc.InsertColumn(0, 'Name')
self.lc.InsertColumn(1, 'Description')
And when I add a item I want them to show the Name parameter and the description:
num_items = self.lc.GetItemCount()
self.lc.InsertStringItem(num_items, "Randomname")
self.lc.SetStringItem(num_items, 1, "Some description here")
Now what I want to do is basically assign something to that item that is not shown so I can access later on the app.
So I would like to add something that is not shown on the app but is on the item value like:
hiddendescription = "Somerandomthing"
Still didn't undestand? Well lets say I add a button to add a item with some other TextCtrls to set the parameters and the TextCtrls parameters are:
"Name"
"Description"
"Hiddendescription"
So then the user fills this textctrls out and clicks the button to create the item, and I basically want only to show the Name and Description and hide the "HiddenDescription" but to do it so I can use it later.
Sorry for explaining more than 1 time on this post but I want to make sure you understand what I pretend to do.
Instead of using the ListCtrl as your data structure, you could keep a separate list/dict of objects that contain all the information you want and refresh the ListCtrl from your other data structure.
For example:
class MyObject(object):
def __init__(self, name, description, hidden_description):
self.name = name
self.description = description
self.hidden_description = hidden_description
Then in your application:
def __init__(self):
self.my_items = {}
self.lc = wx.ListCtrl(self, -1, style=wx.LC_REPORT)
self.lc.InsertColumn(0, 'Name')
self.lc.InsertColumn(1, 'Description')
def addItemToMyListCtrl(self, name, description, hidden):
new_item = MyObject(name, description, hidden)
self.my_items[name] = new_item
self.lc.Append((new_item.name, new_item.description))
Then when you want to use your additional data you can just look up the correct item in the dictionary and your data will be there.
the wxListCtrl lets you associate arbitrary data with an item, that will not be displayed - read the docs for the following methods:
SetItemData
GetItemData
FindItemData
The wxListItem class also has GetData and SetData methods.
You could always set the width of the hidden column to zero, that might accomplish what you want. I just tried it in a C++ (non-wx) program and it worked fine.
wx.ListCtrl doesn't let you associate a python object with an item like wx.TreeCtrl does with its extremely useful SetPyData() method (and corresponding GetPyData()).
I haven't tried it myself, but there is code here that shows how to create a class to mix in python data with a list. Although I'll admit, it's not clear to me how you're meant to use it.
It also might be possible to directly inherit from wx.ListCtrl, and add the appropriate methods, but I haven't seen any attempts at that anywhere, so it may be harder than I'm thinking.
Alternately you can just use SetItemData() to store an int in each item, and use that int to index a dict (or list, if the items are ordered reliably and consistently) that contains the associated objects. tgray has already shown how to do this, and it's listed at the page I link above as well, so I won't go over it again.